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 "serialize.hh"
9 #include "strCat.hh"
10 #include "xrange.hh"
11 #include <cassert>
12 
13 namespace openmsx {
14 
15 IDEHD::IDEHD(const DeviceConfig& config)
16  : HD(config)
17  , AbstractIDEDevice(config.getMotherBoard())
18  , diskManipulator(config.getReactor().getDiskManipulator())
19 {
20  transferSectorNumber = 0; // avoid UMR is serialize()
21  diskManipulator.registerDrive(
22  *this, tmpStrCat(config.getMotherBoard().getMachineID(), "::"));
23 }
24 
26 {
27  diskManipulator.unregisterDrive(*this);
28 }
29 
30 bool IDEHD::isPacketDevice()
31 {
32  return false;
33 }
34 
35 std::string_view IDEHD::getDeviceName()
36 {
37  return "OPENMSX HARD DISK";
38 }
39 
40 void IDEHD::fillIdentifyBlock(AlignedBuffer& buf)
41 {
42  auto totalSectors = getNbSectors();
43  uint16_t heads = 16;
44  uint16_t sectors = 32;
45  auto cylinders = uint16_t(totalSectors / (heads * sectors)); // TODO overflow?
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 
61 unsigned 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 
76 void IDEHD::writeBlockComplete(AlignedBuffer& buf, unsigned count)
77 {
78  try {
79  assert((count % 512) == 0);
80  unsigned 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 
90 void 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 
133 template<typename Archive>
134 void 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
Definition: DeviceConfig.cc:13
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:143
void writeL16(void *p, uint16_t x)
Definition: endian.hh:135
ALWAYS_INLINE unsigned count(const uint8_t *pIn, const uint8_t *pMatch, const uint8_t *pInLimit)
Definition: lz4.cc:207
This file implemented 3 utility functions:
Definition: Autofire.cc:5
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, CassettePlayer, "CassettePlayer")
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:983
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:659
constexpr auto xrange(T e)
Definition: xrange.hh:155