OXYGENHUB
◆ main openmsx / src / cpu / R800.hh
8.6 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef R800_HH
2
#define R800_HH
3
4
#include "CPUClock.hh"
5
#include "CPURegs.hh"
6
#include "Clock.hh"
7
8
#include "inline.hh"
9
#include "narrow.hh"
10
#include "one_of.hh"
11
#include "xrange.hh"
12
13
#include <array>
14
15
namespace openmsx {
16
17
class R800TYPE : public CPUClock
18
{
19
public:
20
	void updateVisiblePage(uint8_t page, uint8_t primarySlot, uint8_t secondarySlot)
21
	{
22
		extraMemoryDelay[page] =
23
			extraMemoryDelays[page][primarySlot][secondarySlot];
24
	}
25
	void setDRAMmode(bool dram)
26
	{
27
		// TODO currently hardcoded, move to config file?
28
		unsigned val = dram ? 0 : 1;
29
		extraMemoryDelays[0][0][0] = val; // BIOS
30
		extraMemoryDelays[1][0][0] = val; // BASIC
31
		extraMemoryDelays[0][3][1] = val; // SUB-ROM
32
		extraMemoryDelays[1][3][1] = val; // KANJI-DRIVER
33
	}
34
35
protected:
36
	template<bool B> struct Normalize { static constexpr bool value = B; };
37
38
	static constexpr int CLOCK_FREQ = 7159090;
39
	static constexpr unsigned HALT_STATES = 1; // TODO check this
40
	static constexpr bool IS_R800 = true;
41
42
	R800TYPE(EmuTime time, Scheduler& scheduler_)
43
		: CPUClock(time, scheduler_)
44
		, lastRefreshTime(time)
45
	{
46
		R800ForcePageBreak();
47
48
		// TODO currently hardcoded, move to config file?
49
		for (auto page : xrange(4)) {
50
			for (auto prim : xrange(4)) {
51
				for (auto sec : xrange(4)) {
52
					extraMemoryDelays[page][prim][sec] = [&] {
53
						if (prim == one_of(1, 2)) {
54
							// external slot
55
							return 2;
56
						} else if ((prim == 3) && (sec == 0)) {
57
							// internal RAM
58
							return 0;
59
						} else {
60
							// internal ROM
61
							return 1;
62
						}
63
					}();
64
				}
65
			}
66
		}
67
		for (auto page : xrange(4)) {
68
			extraMemoryDelay[page] = extraMemoryDelays[page][0][0];
69
		}
70
	}
71
72
	ALWAYS_INLINE void R800ForcePageBreak()
73
	{
74
		lastPage = -1;
75
	}
76
77
	template<bool PRE_PB, bool POST_PB>
78
	ALWAYS_INLINE void PRE_MEM(unsigned address)
79
	{
80
		int newPage = narrow<int>(address >> 8);
81
		if constexpr (PRE_PB) {
82
			// there is a statically predictable page break at this
83
			// point -> 'add(1)' moved to static cost table
84
		} else {
85
			if ((newPage != lastPage) ||
86
			    (extraMemoryDelay[address >> 14])) [[unlikely]] {
87
				add(1);
88
			}
89
		}
90
		if constexpr (!POST_PB) {
91
			lastPage = newPage;
92
		}
93
	}
94
	template<bool POST_PB>
95
	ALWAYS_INLINE void POST_MEM(unsigned address)
96
	{
97
		add(extraMemoryDelay[address >> 14]);
98
		if constexpr (POST_PB) {
99
			R800ForcePageBreak();
100
		}
101
	}
102
	template<bool PRE_PB, bool POST_PB>
103
	ALWAYS_INLINE void PRE_WORD(unsigned address)
104
	{
105
		int newPage = narrow<int>(address >> 8);
106
		if constexpr (PRE_PB) {
107
			// there is a statically predictable page break at this
108
			// point -> 'add(1)' moved to static cost table
109
			if (extraMemoryDelay[address >> 14]) [[unlikely]] {
110
				add(1);
111
			}
112
		} else {
113
			if (extraMemoryDelay[address >> 14]) [[unlikely]] {
114
				add(2);
115
			} else if (newPage != lastPage) [[unlikely]] {
116
				add(1);
117
			}
118
		}
119
		if constexpr (!POST_PB) {
120
			lastPage = newPage;
121
		}
122
	}
123
	template<bool POST_PB>
124
	ALWAYS_INLINE void POST_WORD(unsigned address)
125
	{
126
		add(2 * extraMemoryDelay[address >> 14]);
127
		if constexpr (POST_PB) {
128
			R800ForcePageBreak();
129
		}
130
	}
131
132
	ALWAYS_INLINE void R800Refresh(CPURegs& R)
133
	{
134
		// atoc documentation says refresh every 222 clocks
135
		//  duration:  256/1024KB  13.5 clocks
136
		//             512KB       21.5 clocks
137
		// But 26/210 matches measurements much better
138
		//   (loosely based on old measurements by Jon on his analogue scope)
139
		EmuTime time = getTimeFast();
140
		if (lastRefreshTime.getTicksTill(time) >= 210) [[unlikely]] {
141
			R800RefreshSlow(time, R); // slow-path not inline
142
		}
143
	}
144
	NEVER_INLINE void R800RefreshSlow(EmuTime time, CPURegs& R)
145
	{
146
		do {
147
			lastRefreshTime += 210;
148
		} while (lastRefreshTime.getTicksTill(time) >= 210); [[unlikely]]
149
		waitForEvenCycle(0);
150
		add(25);
151
		R800ForcePageBreak(); // TODO check this
152
		R.incR(1);
153
	}
154
155
	void setTime(EmuTime time)
156
	{
157
		// Base class implementation.
158
		CPUClock::setTime(time);
159
160
		// Otherwise advance_fast() in R800Refresh() above, gets a too
161
		// large time interval.
162
		lastRefreshTime.reset(time);
163
	}
164
165
	ALWAYS_INLINE void setMemPtr(unsigned /*x*/) const { /* nothing*/ }
166
	[[nodiscard]] ALWAYS_INLINE unsigned getMemPtr() const { return 0; } // dummy value
167
168
	static constexpr int I  = 6; // cycles for an I/O operation
169
	static constexpr int O  = 1; // wait for one cycle and wait for next even
170
	                             // clock cycle (to sync with slower IO bus)
171
	                             // the latter part must be implemented
172
	                             // dynamically (not here in static tables)
173
	static constexpr int P  = 1; // cycles for a (statically known) page-break
174
175
	static constexpr int
176
		CC_LD_A_SS   = 1+P+1, CC_LD_A_SS_1  = 1+P,
177
		CC_LD_A_NN   = 3+P+1, CC_LD_A_NN_1  = 1, CC_LD_A_NN_2  = 3+P,
178
		CC_LD_A_I    = 2,
179
		CC_LD_R_R    = 1,
180
		CC_LD_R_N    = 2,     CC_LD_R_N_1   = 1,
181
		CC_LD_R_HL   = 1+P+1, CC_LD_R_HL_1  = 1+P,
182
		CC_LD_R_XIX  = 3+P+1, CC_LD_R_XIX_1 = 1, CC_LD_R_XIX_2 = 3+P, // +1
183
		CC_LD_HL_R   = 1+P+1, CC_LD_HL_R_1  = 1+P,
184
		CC_LD_HL_N   = 2+P+1, CC_LD_HL_N_1  = 1, CC_LD_HL_N_2  = 2+P,
185
		CC_LD_SS_A   = 1+P+1, CC_LD_SS_A_1  = 1+P,
186
		CC_LD_NN_A   = 3+P+1, CC_LD_NN_A_1  = 1, CC_LD_NN_A_2  = 3+P,
187
		CC_LD_XIX_R  = 3+P+1, CC_LD_XIX_R_1 = 1, CC_LD_XIX_R_2 = 3+P, // +1
188
		CC_LD_XIX_N  = 3+P+1, CC_LD_XIX_N_1 = 1, CC_LD_XIX_N_2 = 3+P, // +1
189
		CC_LD_HL_XX  = 3+P+2, CC_LD_HL_XX_1 = 1, CC_LD_HL_XX_2 = 3+P,
190
		CC_LD_SP_HL  = 1,
191
		CC_LD_SS_NN  = 3,     CC_LD_SS_NN_1 = 1,
192
		CC_LD_XX_HL  = 3+P+2, CC_LD_XX_HL_1 = 1, CC_LD_XX_HL_2 = 3+P,
193
194
		CC_CP_R      = 1,
195
		CC_CP_N      = 2,     CC_CP_N_1     = 1,
196
		CC_CP_XHL    = 1+P+1, CC_CP_XHL_1   = 1+P,
197
		CC_CP_XIX    = 3+P+1, CC_CP_XIX_1   = 1, CC_CP_XIX_2   = 3+P, // +1
198
		CC_INC_R     = 1,
199
		CC_INC_XHL   = 1+P+2+P+1, CC_INC_XHL_1 = 1, CC_INC_XHL_2 = 1+P+2+P,
200
		CC_INC_XIX   = 3+P+2+P+1, CC_INC_XIX_1 = 1, EE_INC_XIX   = 2, // +1
201
		CC_INC_SS    = 1,
202
		CC_ADD_HL_SS = 1,
203
		CC_ADC_HL_SS = 2,
204
205
		CC_LDI       = 2+P+1+P+1, CC_LDI_1  = 2+P, CC_LDI_2    = 2+P+1+P,
206
		CC_LDIR      = 2+P+1+P+1,
207
		CC_CPI       = 2+P+2, CC_CPI_1      = 2+P,
208
		CC_CPIR      = 2+P+3, // TODO check
209
210
		CC_PUSH      = 2+P+2, CC_PUSH_1     = 2+P,
211
		CC_POP       = 1+P+2, CC_POP_1      = 1+P,
212
		CC_CALL      = 3+P+2, CC_CALL_1     = 1, EE_CALL       = 1,
213
		CC_CALL_A    = 3+P+2, CC_CALL_B     = 3,
214
		CC_RST       = 2+P+2, // TODO check
215
		CC_RET_A     = 1+P+2, CC_RET_B      = 1, EE_RET_C      = 0,
216
		CC_RETN      = 2+P+2, EE_RETN       = 1, // TODO check
217
		CC_JP_A      = 4,     CC_JP_B       = 3, CC_JP_1       = 1,
218
		CC_JP_HL     = 2,
219
		CC_JR_A      = 3,     CC_JR_B       = 2, CC_JR_1       = 1,
220
		CC_DJNZ      = 3,     EE_DJNZ       = 0,
221
222
		CC_EX_SP_HL  = 1+P+4, CC_EX_SP_HL_1 = 1+P, CC_EX_SP_HL_2 = 1+P+2,
223
224
		CC_BIT_R     = 2,
225
		CC_BIT_XHL   = 2+P+1, CC_BIT_XHL_1  = 2+P,
226
		CC_BIT_XIX   = 3+P+1, CC_BIT_XIX_1  = 3+P, // +1
227
		CC_SET_R     = 2,
228
		CC_SET_XHL   = 2+P+2+P+1, CC_SET_XHL_1 = 2+P, CC_SET_XHL_2 = 2+P+2+P,
229
		CC_SET_XIX   = 3+P+2+P+1, EE_SET_XIX   = 1, // +1
230
231
		CC_RLA       = 1,
232
		CC_RLD       = 2+P+2+P+1, CC_RLD_1  = 2+P, CC_RLD_2    = 2+P+2+P,
233
234
		CC_IN_A_N    = 2+O+I,     CC_IN_A_N_1 = 1,    CC_IN_A_N_2  = 2+O,
235
		CC_IN_R_C    = 2+O+I,     CC_IN_R_C_1 = 2+O,
236
		CC_INI       = 2+O+I+P+1, CC_INI_1    = 2+O,  CC_INI_2     = 2+O+I+P,
237
		CC_INIR      = 2+O+I+P+1, // TODO check
238
		CC_OUT_N_A   = 2+O+I,     CC_OUT_N_A_1 = 1,   CC_OUT_N_A_2 = 2+O,
239
		CC_OUT_C_R   = 2+O+I,     CC_OUT_C_R_1 = 2+O,
240
		CC_OUTI      = 2+P+1+O+I, CC_OUTI_1    = 2+P, CC_OUTI_2    = 2+P+1+O,
241
		CC_OTIR      = 2+P+1+O+I, // TODO check
242
243
		CC_EX        = 1,
244
		CC_NOP       = 1,
245
		CC_CCF       = 1,
246
		CC_SCF       = 1,
247
		CC_DAA       = 1,
248
		CC_NEG       = 2,
249
		CC_CPL       = 1,
250
		CC_DI        = 2,
251
		CC_EI        = 1,
252
		CC_HALT      = 1, // TODO check
253
		CC_IM        = 3,
254
255
		CC_MULUB     = 14,
256
		CC_MULUW     = 36,
257
258
		CC_NMI       = 1+P+2,   EE_NMI_1      = -1,                    // TODO check
259
		CC_IRQ0      = 1+P+2,   EE_IRQ0_1     = -1,                    // TODO check
260
		CC_IRQ1      = 1+P+2,   EE_IRQ1_1     = -1,                    // TODO check
261
		CC_IRQ2      = 1+P+2+P+2, EE_IRQ2_1   = -1, CC_IRQ2_2 = 1+P+2+P, // TODO check
262
263
		CC_MAIN      = 0,
264
		CC_DD        = 1,
265
		CC_PREFIX    = 1,
266
		CC_DD_CB     = 1, // +1
267
		EE_ED        = 1,
268
		CC_RDMEM     = 1,
269
		CC_WRMEM     = 2;
270
271
	template<typename Archive>
272
	void serialize(Archive& ar, unsigned version)
273
	{
274
		CPUClock::serialize(ar, version);
275
		ar.serialize("lastRefreshTime",  lastRefreshTime,
276
		             "lastPage",         lastPage,
277
		             "extraMemoryDelay", extraMemoryDelay);
278
279
		// don't serialize 'extraMemoryDelays', is initialized in
280
		// constructor and setDRAMmode()
281
	}
282
283
private:
284
	Clock<CLOCK_FREQ> lastRefreshTime;
285
	int lastPage;
286
287
	std::array<std::array<std::array<unsigned, 4>, 4>, 4> extraMemoryDelays;
288
	std::array<unsigned, 4> extraMemoryDelay;
289
};
290
291
} // namespace openmsx
292
293
#endif