OXYGENHUB
◆ main openmsx / src / cpu / CPURegs.hh
12.7 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#ifndef CPUREGS_HH
2
#define CPUREGS_HH
3
4
#include "serialize_meta.hh"
5
6
#include <bit>
7
#include <cassert>
8
#include <cstdint>
9
10
namespace openmsx {
11
12
template<std::endian> struct z80regPair_8bit;
13
template<> struct z80regPair_8bit<std::endian::little> { uint8_t l, h; };
14
template<> struct z80regPair_8bit<std::endian::big   > { uint8_t h, l; };
15
union z80regPair {
16
	z80regPair_8bit<std::endian::native> b;
17
	uint16_t w;
18
};
19
20
class CPURegs
21
{
22
public:
23
	explicit CPURegs(bool r800) : Rmask(r800 ? 0xff : 0x7f) {}
24
	[[nodiscard]] uint8_t getA()   const { return AF_.b.h; }
25
	[[nodiscard]] uint8_t getF()   const { return AF_.b.l; }
26
	[[nodiscard]] uint8_t getB()   const { return BC_.b.h; }
27
	[[nodiscard]] uint8_t getC()   const { return BC_.b.l; }
28
	[[nodiscard]] uint8_t getD()   const { return DE_.b.h; }
29
	[[nodiscard]] uint8_t getE()   const { return DE_.b.l; }
30
	[[nodiscard]] uint8_t getH()   const { return HL_.b.h; }
31
	[[nodiscard]] uint8_t getL()   const { return HL_.b.l; }
32
	[[nodiscard]] uint8_t getA2()  const { return AF2_.b.h; }
33
	[[nodiscard]] uint8_t getF2()  const { return AF2_.b.l; }
34
	[[nodiscard]] uint8_t getB2()  const { return BC2_.b.h; }
35
	[[nodiscard]] uint8_t getC2()  const { return BC2_.b.l; }
36
	[[nodiscard]] uint8_t getD2()  const { return DE2_.b.h; }
37
	[[nodiscard]] uint8_t getE2()  const { return DE2_.b.l; }
38
	[[nodiscard]] uint8_t getH2()  const { return HL2_.b.h; }
39
	[[nodiscard]] uint8_t getL2()  const { return HL2_.b.l; }
40
	[[nodiscard]] uint8_t getIXh() const { return IX_.b.h; }
41
	[[nodiscard]] uint8_t getIXl() const { return IX_.b.l; }
42
	[[nodiscard]] uint8_t getIYh() const { return IY_.b.h; }
43
	[[nodiscard]] uint8_t getIYl() const { return IY_.b.l; }
44
	[[nodiscard]] uint8_t getPCh() const { return PC_.b.h; }
45
	[[nodiscard]] uint8_t getPCl() const { return PC_.b.l; }
46
	[[nodiscard]] uint8_t getSPh() const { return SP_.b.h; }
47
	[[nodiscard]] uint8_t getSPl() const { return SP_.b.l; }
48
49
	[[nodiscard]] uint16_t getAF()  const { return AF_.w; }
50
	[[nodiscard]] uint16_t getBC()  const { return BC_.w; }
51
	[[nodiscard]] uint16_t getDE()  const { return DE_.w; }
52
	[[nodiscard]] uint16_t getHL()  const { return HL_.w; }
53
	[[nodiscard]] uint16_t getAF2() const { return AF2_.w; }
54
	[[nodiscard]] uint16_t getBC2() const { return BC2_.w; }
55
	[[nodiscard]] uint16_t getDE2() const { return DE2_.w; }
56
	[[nodiscard]] uint16_t getHL2() const { return HL2_.w; }
57
	[[nodiscard]] uint16_t getIX()  const { return IX_.w; }
58
	[[nodiscard]] uint16_t getIY()  const { return IY_.w; }
59
	[[nodiscard]] uint16_t getPC()  const { return PC_.w; }
60
	[[nodiscard]] uint16_t getSP()  const { return SP_.w; }
61
62
	[[nodiscard]] uint8_t getIM()  const { return IM_; }
63
	[[nodiscard]] uint8_t getI()   const { return I_; }
64
	[[nodiscard]] uint8_t getR()   const { return (R_ & Rmask) | (R2_ & ~Rmask); }
65
	[[nodiscard]] bool getIFF1()     const { return IFF1_; }
66
	[[nodiscard]] bool getIFF2()     const { return IFF2_; }
67
	[[nodiscard]] uint8_t getHALT()     const { return HALT_; }
68
69
	void setA(uint8_t x)   { AF_.b.h = x; }
70
	void setF(uint8_t x)   { AF_.b.l = x; }
71
	void setB(uint8_t x)   { BC_.b.h = x; }
72
	void setC(uint8_t x)   { BC_.b.l = x; }
73
	void setD(uint8_t x)   { DE_.b.h = x; }
74
	void setE(uint8_t x)   { DE_.b.l = x; }
75
	void setH(uint8_t x)   { HL_.b.h = x; }
76
	void setL(uint8_t x)   { HL_.b.l = x; }
77
	void setA2(uint8_t x)  { AF2_.b.h = x; }
78
	void setF2(uint8_t x)  { AF2_.b.l = x; }
79
	void setB2(uint8_t x)  { BC2_.b.h = x; }
80
	void setC2(uint8_t x)  { BC2_.b.l = x; }
81
	void setD2(uint8_t x)  { DE2_.b.h = x; }
82
	void setE2(uint8_t x)  { DE2_.b.l = x; }
83
	void setH2(uint8_t x)  { HL2_.b.h = x; }
84
	void setL2(uint8_t x)  { HL2_.b.l = x; }
85
	void setIXh(uint8_t x) { IX_.b.h = x; }
86
	void setIXl(uint8_t x) { IX_.b.l = x; }
87
	void setIYh(uint8_t x) { IY_.b.h = x; }
88
	void setIYl(uint8_t x) { IY_.b.l = x; }
89
	void setPCh(uint8_t x) { PC_.b.h = x; }
90
	void setPCl(uint8_t x) { PC_.b.l = x; }
91
	void setSPh(uint8_t x) { SP_.b.h = x; }
92
	void setSPl(uint8_t x) { SP_.b.l = x; }
93
94
	void setAF(uint16_t x)  { AF_.w = x; }
95
	void setBC(uint16_t x)  { BC_.w = x; }
96
	void setDE(uint16_t x)  { DE_.w = x; }
97
	void setHL(uint16_t x)  { HL_.w = x; }
98
	void setAF2(uint16_t x) { AF2_.w = x; }
99
	void setBC2(uint16_t x) { BC2_.w = x; }
100
	void setDE2(uint16_t x) { DE2_.w = x; }
101
	void setHL2(uint16_t x) { HL2_.w = x; }
102
	void setIX(uint16_t x)  { IX_.w = x; }
103
	void setIY(uint16_t x)  { IY_.w = x; }
104
	void setPC(uint16_t x)  { PC_.w = x; }
105
	void setSP(uint16_t x)  { SP_.w = x; }
106
107
	void setIM(uint8_t x) { IM_ = x; }
108
	void setI(uint8_t x)  { I_ = x; }
109
	void setR(uint8_t x)  { R_ = x; R2_ = x; }
110
	void setIFF1(bool x)    { IFF1_ = x; }
111
	void setIFF2(bool x)    { IFF2_ = x; }
112
	void setHALT(bool x)    { HALT_ = (HALT_ & ~1) | (x ? 1 : 0); }
113
	void setExtHALT(bool x) { HALT_ = (HALT_ & ~2) | (x ? 2 : 0); }
114
115
	void incR(uint8_t x) { R_ += x; }
116
117
	// Sometimes we need to look at sequences of instructions/actions
118
	// instead of only individual instructions. The most obvious example is
119
	// the non-acceptance of IRQs directly after an EI instruction. But
120
	// also on R800, the timing of the CALL instruction is different when
121
	// it's directly followed by a POP or RET instruction.
122
	//
123
	// The following methods implement this:
124
	// - setCurrentXXX(): set flag for currently executing instruction
125
	// - prevWasXXX(): check whether the last executed instruction had
126
	//                 a specific flag
127
	// - prev2WasXXX(): same but for the 2nd-to-last instruction
128
	// - endInstruction(): this shifts the flags of the current instruction
129
	//                     into the last, last into 2nd-to-last, ...
130
	//
131
	// Optimizations: these sequence flags are relatively infrequently
132
	// needed. So most of the time we want to avoid the (small) overhead of
133
	// maintaining these flags. (CPU emulation is still to most heavy part
134
	// of the total emulation, so every cycle we can save counts). Therefor
135
	// the flags are only shifted in the 'slow' emulation path. This means:
136
	// - After setting a flag we should enter the slow emulation path for a
137
	//   few instructions.
138
	// - Querying the flags should only be done in the slow emulation path.
139
140
	// Set EI-flag on current instruction.
141
	void setCurrentEI() {
142
		prev_ |= 1;
143
	}
144
	// Set LDAI-flag on current instruction.
145
	void setCurrentLDAI() {
146
		prev_ |= 2;
147
	}
148
	// Set CALL-flag on current instruction.
149
	void setCurrentCall() {
150
		prev_ |= 4;
151
	}
152
	// Set POP-RET-flag on current instruction.
153
	void setCurrentPopRet() {
154
		prev_ |= 8;
155
	}
156
157
	// Previous instruction was EI?
158
	[[nodiscard]] bool prevWasEI()   const {
159
		return (prev_ & (1 << 8)) != 0;
160
	}
161
	// Previous instruction was LD A,I or LD A,R?  (only set for Z80)
162
	[[nodiscard]] bool prevWasLDAI() const {
163
		return (prev_ & (2 << 8)) != 0;
164
	}
165
	// Previous-previous instruction was a CALL?  (only set for R800)
166
	[[nodiscard]] bool prev2WasCall() const {
167
		return (prev_ & (4 << (2 * 8))) != 0;
168
	}
169
	// Previous instruction was a POP or RET?  (only set for R800)
170
	[[nodiscard]] bool prevWasPopRet() const {
171
		return (prev_ & (8 << 8)) != 0;
172
	}
173
174
	// Shift flags to the previous instruction positions.
175
	// Clear flags for current instruction.
176
	void endInstruction() {
177
		prev_ <<= 8;
178
	}
179
180
	// Clear all previous-flags (called on reset).
181
	void clearPrevious() {
182
		prev_ = 0;
183
	}
184
185
	// (for debug-only) At the start of an instruction(-block) no flags
186
	// should be set.
187
	void checkNoCurrentFlags() const {
188
		// Exception: we do allow a sloppy POP/RET flag, it only needs
189
		// to be correct after a CALL instruction.
190
		assert((prev_ & 0xF7) == 0);
191
	}
192
193
194
	template<typename Archive>
195
	void serialize(Archive& ar, unsigned version);
196
197
private:
198
	z80regPair PC_;
199
	z80regPair AF_, BC_, DE_, HL_;
200
	z80regPair AF2_, BC2_, DE2_, HL2_;
201
	z80regPair IX_, IY_, SP_;
202
	bool IFF1_, IFF2_;
203
	uint8_t HALT_ = 0;
204
	uint8_t IM_, I_;
205
	uint8_t R_, R2_; // refresh = R & Rmask | R2 & ~Rmask
206
	/*const*/ uint8_t Rmask; // 0x7F for Z80, 0xFF for R800
207
	unsigned prev_;
208
};
209
SERIALIZE_CLASS_VERSION(CPURegs, 3);
210
211
212
/* The above implementation uses a union to access the upper/lower 8 bits in a
213
 * 16 bit value. At some point in the past I replaced this with the
214
 * implementation below because it generated faster code. Though when I measure
215
 * it now, the original version is faster again.
216
 * TODO need to investigate this further.
217
 */
218
#if 0
219
class CPURegs {
220
public:
221
	[[nodiscard]] uint8_t getA()   const { return AF >> 8; }
222
	[[nodiscard]] uint8_t getF()   const { return AF & 255; }
223
	[[nodiscard]] uint8_t getB()   const { return BC >> 8; }
224
	[[nodiscard]] uint8_t getC()   const { return BC & 255; }
225
	[[nodiscard]] uint8_t getD()   const { return DE >> 8; }
226
	[[nodiscard]] uint8_t getE()   const { return DE & 255; }
227
	[[nodiscard]] uint8_t getH()   const { return HL >> 8; }
228
	[[nodiscard]] uint8_t getL()   const { return HL & 255; }
229
	[[nodiscard]] uint8_t getA2()  const { return AF2 >> 8; }
230
	[[nodiscard]] uint8_t getF2()  const { return AF2 & 255; }
231
	[[nodiscard]] uint8_t getB2()  const { return BC2 >> 8; }
232
	[[nodiscard]] uint8_t getC2()  const { return BC2 & 255; }
233
	[[nodiscard]] uint8_t getD2()  const { return DE2 >> 8; }
234
	[[nodiscard]] uint8_t getE2()  const { return DE2 & 255; }
235
	[[nodiscard]] uint8_t getH2()  const { return HL2 >> 8; }
236
	[[nodiscard]] uint8_t getL2()  const { return HL2 & 255; }
237
	[[nodiscard]] uint8_t getIXh() const { return IX >> 8; }
238
	[[nodiscard]] uint8_t getIXl() const { return IX & 255; }
239
	[[nodiscard]] uint8_t getIYh() const { return IY >> 8; }
240
	[[nodiscard]] uint8_t getIYl() const { return IY & 255; }
241
	[[nodiscard]] uint8_t getPCh() const { return PC >> 8; }
242
	[[nodiscard]] uint8_t getPCl() const { return PC & 255; }
243
	[[nodiscard]] uint8_t getSPh() const { return SP >> 8; }
244
	[[nodiscard]] uint8_t getSPl() const { return SP & 255; }
245
	[[nodiscard]] uint16_t getAF()  const { return AF; }
246
	[[nodiscard]] uint16_t getBC()  const { return BC; }
247
	[[nodiscard]] uint16_t getDE()  const { return DE; }
248
	[[nodiscard]] uint16_t getHL()  const { return HL; }
249
	[[nodiscard]] uint16_t getAF2() const { return AF2; }
250
	[[nodiscard]] uint16_t getBC2() const { return BC2; }
251
	[[nodiscard]] uint16_t getDE2() const { return DE2; }
252
	[[nodiscard]] uint16_t getHL2() const { return HL2; }
253
	[[nodiscard]] uint16_t getIX()  const { return IX; }
254
	[[nodiscard]] uint16_t getIY()  const { return IY; }
255
	[[nodiscard]] uint16_t getPC()  const { return PC; }
256
	[[nodiscard]] uint16_t getSP()  const { return SP; }
257
	[[nodiscard]] uint8_t getIM()  const { return IM; }
258
	[[nodiscard]] uint8_t getI()   const { return I; }
259
	[[nodiscard]] uint8_t getR()   const { return (R & 0x7F) | (R2 & 0x80); }
260
	[[nodiscard]] bool getIFF1()     const { return IFF1; }
261
	[[nodiscard]] bool getIFF2()     const { return IFF2; }
262
	[[nodiscard]] bool getHALT()     const { return HALT; }
263
264
	void setA(uint8_t x)   { AF = (AF & 0x00FF) | (x << 8); }
265
	void setF(uint8_t x)   { AF = (AF & 0xFF00) | x; }
266
	void setB(uint8_t x)   { BC = (BC & 0x00FF) | (x << 8); }
267
	void setC(uint8_t x)   { BC = (BC & 0xFF00) | x; }
268
	void setD(uint8_t x)   { DE = (DE & 0x00FF) | (x << 8); }
269
	void setE(uint8_t x)   { DE = (DE & 0xFF00) | x; }
270
	void setH(uint8_t x)   { HL = (HL & 0x00FF) | (x << 8); }
271
	void setL(uint8_t x)   { HL = (HL & 0xFF00) | x; }
272
	void setA2(uint8_t x)  { AF2 = (AF2 & 0x00FF) | (x << 8); }
273
	void setF2(uint8_t x)  { AF2 = (AF2 & 0xFF00) | x; }
274
	void setB2(uint8_t x)  { BC2 = (BC2 & 0x00FF) | (x << 8); }
275
	void setC2(uint8_t x)  { BC2 = (BC2 & 0xFF00) | x; }
276
	void setD2(uint8_t x)  { DE2 = (DE2 & 0x00FF) | (x << 8); }
277
	void setE2(uint8_t x)  { DE2 = (DE2 & 0xFF00) | x; }
278
	void setH2(uint8_t x)  { HL2 = (HL2 & 0x00FF) | (x << 8); }
279
	void setL2(uint8_t x)  { HL2 = (HL2 & 0xFF00) | x; }
280
	void setIXh(uint8_t x) { IX = (IX & 0x00FF) | (x << 8); }
281
	void setIXl(uint8_t x) { IX = (IX & 0xFF00) | x; }
282
	void setIYh(uint8_t x) { IY = (IY & 0x00FF) | (x << 8); }
283
	void setIYl(uint8_t x) { IY = (IY & 0xFF00) | x; }
284
	void setPCh(uint8_t x) { PC = (PC & 0x00FF) | (x << 8); }
285
	void setPCl(uint8_t x) { PC = (PC & 0xFF00) | x; }
286
	void setSPh(uint8_t x) { SP = (SP & 0x00FF) | (x << 8); }
287
	void setSPl(uint8_t x) { SP = (SP & 0xFF00) | x; }
288
	void setAF(uint16_t x)  { AF = x; }
289
	void setBC(uint16_t x)  { BC = x; }
290
	void setDE(uint16_t x)  { DE = x; }
291
	void setHL(uint16_t x)  { HL = x; }
292
	void setAF2(uint16_t x) { AF2 = x; }
293
	void setBC2(uint16_t x) { BC2 = x; }
294
	void setDE2(uint16_t x) { DE2 = x; }
295
	void setHL2(uint16_t x) { HL2 = x; }
296
	void setIX(uint16_t x)  { IX = x; }
297
	void setIY(uint16_t x)  { IY = x; }
298
	void setPC(uint16_t x)  { PC = x; }
299
	void setSP(uint16_t x)  { SP = x; }
300
	void setIM(uint8_t x)  { IM = x; }
301
	void setI(uint8_t x)   { I = x; }
302
	void setR(uint8_t x)   { R = x; R2 = x; }
303
	void setIFF1(bool x)     { IFF1 = x; }
304
	void setIFF2(bool x)     { IFF2 = x; }
305
	void setHALT(bool x)     { HALT = x; }
306
307
	void incR(uint8_t x) { R += x; }
308
309
private:
310
	uint16_t AF, BC, DE, HL;
311
	uint16_t AF2, BC2, DE2, HL2;
312
	uint16_t IX, IY, PC, SP;
313
	bool IFF1, IFF2, HALT;
314
	uint8_t IM, I;
315
	uint8_t R, R2; // refresh = R&127 | R2&128
316
};
317
#endif
318
319
} // namespace openmsx
320
321
#endif