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 <cassert>
11 
12 namespace openmsx {
13 
14 IDEHD::IDEHD(const DeviceConfig& config)
15  : HD(config)
16  , AbstractIDEDevice(config.getMotherBoard())
17  , diskManipulator(config.getReactor().getDiskManipulator())
18 {
19  transferSectorNumber = 0; // avoid UMR is serialize()
20  diskManipulator.registerDrive(
21  *this, strCat(config.getMotherBoard().getMachineID(), "::"));
22 }
23 
25 {
26  diskManipulator.unregisterDrive(*this);
27 }
28 
29 bool IDEHD::isPacketDevice()
30 {
31  return false;
32 }
33 
34 const std::string& IDEHD::getDeviceName()
35 {
36  static const std::string NAME = "OPENMSX HARD DISK";
37  return NAME;
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 (unsigned i = 0; i < num; ++i) {
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 readSector(size_t sector, SectorBuffer &buf)
IDEHD(const IDEHD &)=delete
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, CassettePlayer, "CassettePlayer")
void startLongReadTransfer(unsigned count)
Indicates the start of a read data transfer which uses blocks.
unsigned getSectorNumber() const
Creates an LBA sector address from the contents of the sectorNumReg, cylinderLowReg, cylinderHighReg and devHeadReg registers.
unsigned getNumSectors() const
Gets the number of sectors indicated by the sector count register.
~IDEHD() override
Definition: IDEHD.cc:24
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
void registerDrive(DiskContainer &drive, const std::string &prefix)
void abortWriteTransfer(byte error)
Aborts the write transfer in progress.
void setSectorNumber(unsigned lba)
Writes a 28-bit LBA sector number in the registers.
void abortReadTransfer(byte error)
Aborts the read transfer in progress.
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
void startWriteTransfer(unsigned count)
Indicates the start of a write data transfer.
void setError(byte error)
Indicates an error: sets error register, error flag, aborts transfers.
auto count(InputRange &&range, const T &value)
Definition: ranges.hh:209
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1006
void serialize(Archive &ar, unsigned version)
Definition: IDEHD.cc:134
std::string strCat(Ts &&...ts)
Definition: strCat.hh:577
virtual void executeCommand(byte cmd)
Starts execution of an IDE command.
string_view getMachineID() const
MSXMotherBoard & getMotherBoard() const
Definition: DeviceConfig.cc:13
void unregisterDrive(DiskContainer &drive)
void writeSector(size_t sector, const SectorBuffer &buf)
void setByteCount(unsigned count)
Writes the byte count of a packet transfer in the registers.