OXYGENHUB
◆ main openmsx / src / ide / AbstractIDEDevice.hh
7.7 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef ABSTRACTIDEDEVICE_HH
2
#define ABSTRACTIDEDEVICE_HH
3
4
#include "IDEDevice.hh"
5
6
#include "serialize_meta.hh"
7
8
#include "AlignedBuffer.hh"
9
10
namespace openmsx {
11
12
class MSXMotherBoard;
13
14
class AbstractIDEDevice : public IDEDevice
15
{
16
public:
17
	void reset(EmuTime time) override;
18
19
	[[nodiscard]] uint16_t readData(EmuTime time) override;
20
	[[nodiscard]] uint8_t readReg(uint4_t reg, EmuTime time) override;
21
22
	void writeData(uint16_t value, EmuTime time) override;
23
	void writeReg(uint4_t reg, uint8_t value, EmuTime time) override;
24
25
	template<typename Archive>
26
	void serialize(Archive& ar, unsigned version);
27
28
protected:
29
	// Bit flags for the status register:
30
	static constexpr uint8_t DRDY = 0x40;
31
	static constexpr uint8_t DSC = 0x10;
32
	static constexpr uint8_t DRQ = 0x08;
33
	static constexpr uint8_t ERR = 0x01;
34
35
	// Bit flags for the error register:
36
	static constexpr uint8_t UNC = 0x40;
37
	static constexpr uint8_t IDNF = 0x10;
38
	static constexpr uint8_t ABORT = 0x04;
39
40
	explicit AbstractIDEDevice(MSXMotherBoard& motherBoard);
41
	~AbstractIDEDevice() override = default;
42
43
	/** Is this device a packet (ATAPI) device?
44
	  * @return True iff this device supports the packet commands.
45
	  */
46
	[[nodiscard]] virtual bool isPacketDevice() = 0;
47
48
	/** Gets the device name to insert as "model number" into the identify
49
	  * block.
50
	  * @return An ASCII string, up to 40 characters long.
51
	  */
52
	[[nodiscard]] virtual std::string_view getDeviceName() = 0;
53
54
	/** Tells a subclass to fill the device specific parts of the identify
55
	  * block located in the buffer.
56
	  * The generic part is already written there.
57
	  * @param buffer Array of 512 bytes.
58
	  */
59
	virtual void fillIdentifyBlock(AlignedBuffer& buffer) = 0;
60
61
	/** Called when a block of read data should be buffered by the controller:
62
	  * when the buffer is empty or at the start of the transfer.
63
	  * @param buffer Pointer to the start of a byte array.
64
	  * @param count Number of bytes to be filled by this method.
65
	  *   This number will not exceed the array size nor the transfer length.
66
	  * @return The number of bytes that was added to the array,
67
	  *   or 0 if the transfer was aborted (the implementation of this method
68
	  *   must set the relevant error flags as well).
69
	  */
70
	[[nodiscard]] virtual unsigned readBlockStart(AlignedBuffer& buffer, unsigned count) = 0;
71
72
	/** Called when a read transfer completes.
73
	  * The default implementation does nothing.
74
	  */
75
	virtual void readEnd();
76
77
	/** Called when a block of written data has been buffered by the controller:
78
	  * when the buffer is full or at the end of the transfer.
79
	  * @param buffer Pointer to the start of a byte array.
80
	  * @param count Number of data bytes in the array.
81
	  */
82
	virtual void writeBlockComplete(AlignedBuffer& buffer, unsigned count) = 0;
83
84
	/** Starts execution of an IDE command.
85
	  * Override this to implement additional commands and make sure you call
86
	  * the superclass implementation for all commands that you don't handle.
87
	  */
88
	virtual void executeCommand(uint8_t cmd);
89
90
	/** Indicates an error: sets error register, error flag, aborts transfers.
91
	  * @param error Value to be written to the error register.
92
	  */
93
	void setError(uint8_t error);
94
95
	/** Creates an LBA sector address from the contents of the sectorNumReg,
96
	  * cylinderLowReg, cylinderHighReg and devHeadReg registers.
97
	  */
98
	[[nodiscard]] unsigned getSectorNumber() const;
99
100
	/** Gets the number of sectors indicated by the sector count register.
101
	  */
102
	[[nodiscard]] unsigned getNumSectors() const;
103
104
	/** Writes the interrupt reason register.
105
	  * This is the same as register as sector count, but serves a different
106
	  * purpose.
107
	  */
108
	void setInterruptReason(uint8_t value);
109
110
	/** Reads the byte count limit of a packet transfer in the registers.
111
	  * The cylinder low/high registers are used for this.
112
	  */
113
	[[nodiscard]] unsigned getByteCount() const;
114
115
	/** Writes the byte count of a packet transfer in the registers.
116
	  * The cylinder low/high registers are used for this.
117
	  */
118
	void setByteCount(unsigned count);
119
120
	/** Writes a 28-bit LBA sector number in the registers.
121
	  * The cylinder low/high registers are used for this.
122
	  */
123
	void setSectorNumber(unsigned lba);
124
125
	/** Indicates the start of a read data transfer which uses blocks.
126
	  * The readBlockStart() method is called at the start of each block.
127
	  * The first block will be read immediately, so make sure you initialise
128
	  * all variables needed by readBlockStart() before calling this method.
129
	  * @param count Total number of bytes to transfer.
130
	  */
131
	void startLongReadTransfer(unsigned count);
132
133
	/** Indicates the start of a read data transfer where all data fits
134
	  * into the buffer at once.
135
	  * @param count Total number of bytes to transfer.
136
	  * @return Pointer to the start of the buffer.
137
	  *   The caller should write the data there.
138
	  *   The relevant part of the buffer contains zeroes.
139
	  */
140
	[[nodiscard]] AlignedBuffer& startShortReadTransfer(unsigned count);
141
142
	/** Aborts the read transfer in progress.
143
	  */
144
	void abortReadTransfer(uint8_t error);
145
146
	/** Indicates the start of a write data transfer.
147
	  * @param count Total number of bytes to transfer.
148
	  */
149
	void startWriteTransfer(unsigned count);
150
151
	/** Aborts the write transfer in progress.
152
	  */
153
	void abortWriteTransfer(uint8_t error);
154
155
	[[nodiscard]] uint8_t getFeatureReg() const { return featureReg; }
156
	void setLBALow (uint8_t value) { sectorNumReg    = value; }
157
	void setLBAMid (uint8_t value) { cylinderLowReg  = value; }
158
	void setLBAHigh(uint8_t value) { cylinderHighReg = value; }
159
160
	[[nodiscard]] MSXMotherBoard& getMotherBoard() const { return motherBoard; }
161
162
private:
163
	/** Perform diagnostic and return result.
164
	  * Actually, just return success, because we don't emulate faulty hardware.
165
	  */
166
	[[nodiscard]] uint8_t diagnostic() const;
167
168
	/** Puts special values in the sector address, sector count and device
169
	  * registers to identify the type of device.
170
	  * @param preserveDevice If true, preserve the value of the DEV bit;
171
	  *   if false, set the DEV bit to 0.
172
	  */
173
	void createSignature(bool preserveDevice = false);
174
175
	/** Puts the output for the IDENTIFY DEVICE command in the buffer.
176
	  * @param buffer Pointer to the start of the buffer.
177
	  *   The buffer must be at least 512 bytes in size.
178
	  */
179
	void createIdentifyBlock(AlignedBuffer& buffer);
180
181
	/** Initialises registers for a data transfer.
182
	  */
183
	void startReadTransfer();
184
185
	/** Initialises buffer related variables for the next data block.
186
	  * Calls readBlockStart() to deliver the actual data.
187
	  */
188
	void readNextBlock();
189
190
	/** Indicates that a read transfer starts.
191
	  */
192
	void setTransferRead(bool status);
193
194
	/** Initialises buffer related variables for the next data block.
195
	  * Make sure transferCount is initialised before calling this method.
196
	  */
197
	void writeNextBlock();
198
199
	/** Indicates that a write transfer starts.
200
	  */
201
	void setTransferWrite(bool status);
202
203
private:
204
	MSXMotherBoard& motherBoard;
205
206
	/** Data buffer shared by all transfers.
207
	  * The size must be a multiple of 512.
208
	  * Right now I don't see any reason to make it larger than the minimum
209
	  * size of 1 * 512.
210
	  */
211
	AlignedByteArray<512> buffer;
212
213
	/** Index of current read/write position in the buffer.
214
	  */
215
	unsigned transferIdx = 0; // avoid UMR on serialize
216
217
	/** Number of bytes remaining in the buffer.
218
	  */
219
	unsigned bufferLeft = 0;
220
221
	/** Number of bytes remaining in the transfer after this buffer.
222
	  * (total bytes remaining == transferCount + bufferLeft)
223
	  */
224
	unsigned transferCount = 0;
225
226
	// ATA registers:
227
	uint8_t errorReg;
228
	uint8_t sectorCountReg;
229
	uint8_t sectorNumReg;
230
	uint8_t cylinderLowReg;
231
	uint8_t cylinderHighReg;
232
	uint8_t devHeadReg;
233
	uint8_t statusReg;
234
	uint8_t featureReg;
235
236
	bool transferRead = false;
237
	bool transferWrite = false;
238
};
239
240
REGISTER_BASE_NAME_HELPER(AbstractIDEDevice, "IDEDevice");
241
242
} // namespace openmsx
243
244
#endif // ABSTRACTIDEDEVICE_HH