openMSX
IDEHD.cc
Go to the documentation of this file.
1#include "IDEHD.hh"
2#include "MSXException.hh"
3#include "DeviceConfig.hh"
4#include "MSXMotherBoard.hh"
5#include "Reactor.hh"
6#include "DiskManipulator.hh"
7#include "endian.hh"
8#include "narrow.hh"
9#include "serialize.hh"
10#include "strCat.hh"
11#include "xrange.hh"
12#include <cassert>
13
14namespace openmsx {
15
17 : HD(config)
18 , AbstractIDEDevice(config.getMotherBoard())
19 , diskManipulator(config.getReactor().getDiskManipulator())
20{
21 diskManipulator.registerDrive(
22 *this, tmpStrCat(config.getMotherBoard().getMachineID(), "::"));
23}
24
26{
27 diskManipulator.unregisterDrive(*this);
28}
29
30bool IDEHD::isPacketDevice()
31{
32 return false;
33}
34
35std::string_view IDEHD::getDeviceName()
36{
37 return "OPENMSX HARD DISK";
38}
39
40void IDEHD::fillIdentifyBlock(AlignedBuffer& buf)
41{
42 auto totalSectors = getNbSectors();
43 const uint16_t heads = 16;
44 const uint16_t sectors = 32;
45 auto cylinders = narrow<uint16_t>(totalSectors / size_t(heads * sectors));
46 Endian::writeL16(&buf[1 * 2], cylinders);
47 Endian::writeL16(&buf[3 * 2], heads);
48 Endian::writeL16(&buf[6 * 2], sectors);
49
50 buf[47 * 2 + 0] = 16; // max sector transfer per interrupt
51 buf[47 * 2 + 1] = 0x80; // specced value
52
53 // .... 1...: IORDY supported (hardware signal used by PIO modes >3)
54 // .... ..1.: LBA supported
55 buf[49 * 2 + 1] = 0x0A;
56
57 // TODO check for overflow
58 Endian::writeL32(&buf[60 * 2], unsigned(totalSectors));
59}
60
61unsigned IDEHD::readBlockStart(AlignedBuffer& buf, unsigned count)
62{
63 try {
64 assert(count >= 512);
65 (void)count; // avoid warning
66 readSector(transferSectorNumber,
67 *aligned_cast<SectorBuffer*>(buf));
68 ++transferSectorNumber;
69 return 512;
70 } catch (MSXException&) {
72 return 0;
73 }
74}
75
76void IDEHD::writeBlockComplete(AlignedBuffer& buf, unsigned count)
77{
78 try {
79 assert((count % 512) == 0);
80 size_t num = count / 512;
81 for (auto i : xrange(num)) {
82 writeSector(transferSectorNumber++,
83 *aligned_cast<SectorBuffer*>(buf + 512 * i));
84 }
85 } catch (MSXException&) {
87 }
88}
89
90void IDEHD::executeCommand(byte cmd)
91{
92 if (0x10 <= cmd && cmd < 0x20) {
93 // Recalibrate
94 setError(0);
95 setByteCount(0);
96 return;
97 }
98 switch (cmd) {
99 case 0x20: // Read Sector
100 case 0x21: // Read Sector without Retry
101 case 0x30: // Write Sector
102 case 0x31: { // Write Sector without Retry
103 unsigned sectorNumber = getSectorNumber();
104 unsigned numSectors = getNumSectors();
105 if ((sectorNumber + numSectors) > getNbSectors()) {
106 // Note: The original code set ABORT as well, but that is not
107 // allowed according to the spec.
108 setError(IDNF);
109 break;
110 }
111 transferSectorNumber = sectorNumber;
112 if (cmd < 0x30) {
113 startLongReadTransfer(numSectors * 512);
114 } else {
115 startWriteTransfer(numSectors * 512);
116 }
117 break;
118 }
119
120 case 0xF8: // Read Native Max Address
121 // We don't support the Host Protected Area feature set, but SymbOS
122 // uses only this particular command, so we support just this one.
123 // TODO this only supports 28-bit sector numbers
124 setSectorNumber(unsigned(getNbSectors()));
125 break;
126
127 default: // all others
129 }
130}
131
132
133template<typename Archive>
134void IDEHD::serialize(Archive& ar, unsigned /*version*/)
135{
136 // don't serialize SectorAccessibleDisk, DiskContainer base classes
137 ar.template serializeBase<HD>(*this);
138 ar.template serializeBase<AbstractIDEDevice>(*this);
139 ar.serialize("transferSectorNumber", transferSectorNumber);
140}
143
144} // namespace openmsx
void startWriteTransfer(unsigned count)
Indicates the start of a write data transfer.
static constexpr byte UNC
void startLongReadTransfer(unsigned count)
Indicates the start of a read data transfer which uses blocks.
virtual void executeCommand(byte cmd)
Starts execution of an IDE command.
void setSectorNumber(unsigned lba)
Writes a 28-bit LBA sector number in the registers.
void setByteCount(unsigned count)
Writes the byte count of a packet transfer in the registers.
unsigned getNumSectors() const
Gets the number of sectors indicated by the sector count register.
void abortReadTransfer(byte error)
Aborts the read transfer in progress.
void setError(byte error)
Indicates an error: sets error register, error flag, aborts transfers.
void abortWriteTransfer(byte error)
Aborts the write transfer in progress.
static constexpr byte IDNF
unsigned getSectorNumber() const
Creates an LBA sector address from the contents of the sectorNumReg, cylinderLowReg,...
MSXMotherBoard & getMotherBoard() const
void unregisterDrive(DiskContainer &drive)
void registerDrive(DiskContainer &drive, std::string_view prefix)
~IDEHD() override
Definition IDEHD.cc:25
void serialize(Archive &ar, unsigned version)
Definition IDEHD.cc:134
IDEHD(const IDEHD &)=delete
std::string_view getMachineID() const
void readSector(size_t sector, SectorBuffer &buf)
void writeSector(size_t sector, const SectorBuffer &buf)
void writeL32(void *p, uint32_t x)
Definition endian.hh:151
void writeL16(void *p, uint16_t x)
Definition endian.hh:143
ALWAYS_INLINE unsigned count(const uint8_t *pIn, const uint8_t *pMatch, const uint8_t *pInLimit)
Definition lz4.cc:147
This file implemented 3 utility functions:
Definition Autofire.cc:9
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define REGISTER_POLYMORPHIC_INITIALIZER(BASE, CLASS, NAME)
TemporaryString tmpStrCat(Ts &&... ts)
Definition strCat.hh:742
constexpr auto xrange(T e)
Definition xrange.hh:132