OXYGENHUB
◆ main openmsx / src / cpu / MSXCPU.hh
7.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef MSXCPU_HH
2
#define MSXCPU_HH
3
4
#include "CacheLine.hh"
5
6
#include "BooleanSetting.hh"
7
#include "EmuTime.hh"
8
#include "InfoTopic.hh"
9
#include "Observer.hh"
10
#include "SimpleDebuggable.hh"
11
#include "TclCallback.hh"
12
#include "serialize_meta.hh"
13
14
#include <array>
15
#include <cstdint>
16
#include <memory>
17
#include <span>
18
19
namespace openmsx {
20
21
class MSXMotherBoard;
22
class MSXCPUInterface;
23
class CPUClock;
24
class CPURegs;
25
class Z80TYPE;
26
class R800TYPE;
27
template<typename T> class CPUCore;
28
class TclObject;
29
class Interpreter;
30
31
class MSXCPU final : private Observer<Setting>
32
{
33
public:
34
	enum class Type : uint8_t { Z80, R800 };
35
36
	explicit MSXCPU(MSXMotherBoard& motherboard);
37
	~MSXCPU();
38
39
	/** Reset CPU.
40
	  * Requires CPU is not in the middle of an instruction,
41
	  * so exitCPULoop was called and execute() method returned.
42
	 */
43
	void doReset(EmuTime time);
44
45
	/** Switch between Z80/R800. */
46
	void setActiveCPU(Type cpu);
47
48
	/** Sets DRAM or ROM mode (influences memory access speed for R800). */
49
	void setDRAMmode(bool dram);
50
51
	/** Inform CPU of bank switch. This will invalidate memory cache and
52
	  * update memory timings on R800. */
53
	void updateVisiblePage(uint8_t page, uint8_t primarySlot, uint8_t secondarySlot);
54
55
	/** Invalidate the CPU its cache for the interval [start, start + size)
56
	  * For example MSXMemoryMapper and MSXGameCartridge need to call this
57
	  * method when a 'memory switch' occurs. */
58
	void invalidateAllSlotsRWCache(uint16_t start, unsigned size);
59
60
	/** Similar to the method above, but only invalidates one specific slot.
61
	  * One small tweak: lines that are in 'disallowRead/Write' are
62
	  * immediately marked as 'non-cacheable' instead of (first) as
63
	  * 'unknown'.
64
	  */
65
	void invalidateRWCache(unsigned start, unsigned size, int ps, int ss,
66
	                       std::span<const uint8_t, 256> disallowRead,
67
	                       std::span<const uint8_t, 256> disallowWrite);
68
	void invalidateRCache (unsigned start, unsigned size, int ps, int ss,
69
	                       std::span<const uint8_t, 256> disallowRead,
70
	                       std::span<const uint8_t, 256> disallowWrite);
71
	void invalidateWCache (unsigned start, unsigned size, int ps, int ss,
72
	                       std::span<const uint8_t, 256> disallowRead,
73
	                       std::span<const uint8_t, 256> disallowWrite);
74
75
	/** Fill the read and write cache lines for a specific slot with the
76
	 * specified value. Except for the lines where the corresponding
77
	 * 'disallow{Read,Write}' array is non-zero, those lines are marked
78
	 * non-cacheable.
79
	 * This is useful on e.g. a memory mapper bank switch because:
80
	 * - Marking the lines 'unknown' (as done by invalidateMemCache) is
81
	 *   as much work as directly setting the correct value.
82
	 * - Directly setting the correct value saves work later on.
83
	 */
84
	void fillRWCache(unsigned start, unsigned size, const uint8_t* rData, uint8_t* wData, int ps, int ss,
85
	                 std::span<const uint8_t, 256> disallowRead,
86
	                 std::span<const uint8_t, 256> disallowWrite);
87
	void fillRCache (unsigned start, unsigned size, const uint8_t* rData, int ps, int ss,
88
	                 std::span<const uint8_t, 256> disallowRead,
89
	                 std::span<const uint8_t, 256> disallowWrite);
90
	void fillWCache (unsigned start, unsigned size, uint8_t* wData, int ps, int ss,
91
	                 std::span<const uint8_t, 256> disallowRead,
92
	                 std::span<const uint8_t, 256> disallowWrite);
93
94
	/** This method raises a maskable interrupt. A device may call this
95
	  * method more than once. If the device wants to lower the
96
	  * interrupt again it must call the lowerIRQ() method exactly as
97
	  * many times.
98
	  * Before using this method take a look at IRQHelper. */
99
	void raiseIRQ();
100
101
	/** This methods lowers the maskable interrupt again. A device may never
102
	  * call this method more often than it called the method
103
	  * raiseIRQ().
104
	  * Before using this method take a look at IRQHelper. */
105
	void lowerIRQ();
106
107
	/** This method raises a non-maskable interrupt. A device may call this
108
	  * method more than once. If the device wants to lower the
109
	  * interrupt again it must call the lowerNMI() method exactly as
110
	  * many times.
111
	  * Before using this method take a look at IRQHelper. */
112
	void raiseNMI();
113
114
	/** This methods lowers the non-maskable interrupt again. A device may never
115
	  * call this method more often than it called the method
116
	  * raiseNMI().
117
	  * Before using this method take a look at IRQHelper. */
118
	void lowerNMI();
119
120
	/** Should only be used from within a MSXDevice::readMem() method.
121
	  * Returns true if that read was the first byte of an instruction
122
	  * (the Z80 M1 pin is active). This implementation is not 100%
123
	  * accurate, but good enough for now.
124
	  */
125
	[[nodiscard]] bool isM1Cycle(unsigned address) const;
126
127
	/** See CPUCore::exitCPULoopSync() */
128
	void exitCPULoopSync();
129
	/** See CPUCore::exitCPULoopAsync() */
130
	void exitCPULoopAsync();
131
132
	/** Is the R800 currently active? */
133
	[[nodiscard]] bool isR800Active() const { return !z80Active; }
134
135
	/** Switch the Z80 clock freq. */
136
	void setZ80Freq(unsigned freq);
137
138
	void setInterface(MSXCPUInterface* interface);
139
140
	/** (un)pause CPU. During pause the CPU executes NOP instructions
141
	  * continuously (just like during HALT). Used by turbor hw pause. */
142
	void setPaused(bool paused);
143
144
	void setNextSyncPoint(EmuTime time);
145
146
	void wait(EmuTime time);
147
	EmuTime waitCyclesZ80(EmuTime time, unsigned cycles);
148
	EmuTime waitCyclesR800(EmuTime time, unsigned cycles);
149
150
	[[nodiscard]] CPURegs& getRegisters();
151
152
	[[nodiscard]] auto* getZ80() { return z80.get(); }
153
	[[nodiscard]] auto* getR800() { return r800.get(); }
154
155
	template<typename Archive>
156
	void serialize(Archive& ar, unsigned version);
157
158
private:
159
	void invalidateMemCacheSlot();
160
161
	// only for MSXMotherBoard
162
	void execute(bool fastForward);
163
	friend class MSXMotherBoard;
164
165
	/** The time returned by this method is not safe to use for Scheduler
166
	  * or IO related stuff (because it can sometimes already be higher then
167
	  * still to be scheduled sync points). Use Scheduler::getCurrentTime()
168
	  * instead.
169
	  * TODO is this comment still true? */
170
	EmuTime getCurrentTime() const;
171
172
	// Observer<Setting>
173
	void update(const Setting& setting) noexcept override;
174
175
	template<bool READ, bool WRITE, bool SUB_START>
176
	void setRWCache(unsigned start, unsigned size, const uint8_t* rData, uint8_t* wData, int ps, int ss,
177
	                std::span<const uint8_t, 256> disallowRead, std::span<const uint8_t, 256> disallowWrite);
178
179
private:
180
	MSXMotherBoard& motherboard;
181
	TclCallback diHaltCallback;
182
	const std::unique_ptr<CPUCore<Z80TYPE>> z80;
183
	const std::unique_ptr<CPUCore<R800TYPE>> r800; // can be nullptr
184
185
	std::array<std::array<const uint8_t*, CacheLine::NUM>, 16> slotReadLines;
186
	std::array<std::array<      uint8_t*, CacheLine::NUM>, 16> slotWriteLines;
187
	std::array<uint8_t, 4> slots; // active slot for page (= 4 * primSlot + secSlot)
188
189
	struct TimeInfoTopic final : InfoTopic {
190
		explicit TimeInfoTopic(InfoCommand& machineInfoCommand);
191
		void execute(std::span<const TclObject> tokens,
192
			     TclObject& result) const override;
193
		[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
194
	} timeInfo;
195
196
	class CPUFreqInfoTopic final : public InfoTopic {
197
	public:
198
		CPUFreqInfoTopic(InfoCommand& machineInfoCommand,
199
				 const std::string& name, CPUClock& clock);
200
		void execute(std::span<const TclObject> tokens,
201
			     TclObject& result) const override;
202
		[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
203
	private:
204
		CPUClock& clock;
205
	};
206
	CPUFreqInfoTopic                        z80FreqInfo;  // always present
207
	const std::unique_ptr<CPUFreqInfoTopic> r800FreqInfo; // can be nullptr
208
209
	struct Debuggable final : SimpleDebuggable {
210
		explicit Debuggable(MSXMotherBoard& motherboard);
211
		[[nodiscard]] uint8_t read(unsigned address) override;
212
		void write(unsigned address, uint8_t value) override;
213
	} debuggable;
214
215
	bool z80Active{true};
216
	bool newZ80Active{true};
217
218
	MSXCPUInterface* interface{nullptr}; // only used for debug
219
};
220
SERIALIZE_CLASS_VERSION(MSXCPU, 3);
221
222
} // namespace openmsx
223
224
#endif