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 , devName(config.getChildData("name", "openMSX hard disk"))
21{
22 diskManipulator.registerDrive(
23 *this, tmpStrCat(config.getMotherBoard().getMachineID(), "::"));
24}
25
27{
28 diskManipulator.unregisterDrive(*this);
29}
30
31bool IDEHD::isPacketDevice()
32{
33 return false;
34}
35
36std::string_view IDEHD::getDeviceName()
37{
38 return devName;
39}
40
41void IDEHD::fillIdentifyBlock(AlignedBuffer& buf)
42{
43 auto totalSectors = getNbSectors();
44 const uint16_t heads = 16;
45 const uint16_t sectors = 32;
46 auto cylinders = narrow<uint16_t>(totalSectors / size_t(heads * sectors));
47 Endian::writeL16(&buf[1 * 2], cylinders);
48 Endian::writeL16(&buf[3 * 2], heads);
49 Endian::writeL16(&buf[6 * 2], sectors);
50
51 buf[47 * 2 + 0] = 16; // max sector transfer per interrupt
52 buf[47 * 2 + 1] = 0x80; // specced value
53
54 // .... 1...: IORDY supported (hardware signal used by PIO modes >3)
55 // .... ..1.: LBA supported
56 buf[49 * 2 + 1] = 0x0A;
57
58 // TODO check for overflow
59 Endian::writeL32(&buf[60 * 2], unsigned(totalSectors));
60}
61
62unsigned IDEHD::readBlockStart(AlignedBuffer& buf, unsigned count)
63{
64 try {
65 assert(count >= 512);
66 (void)count; // avoid warning
67 readSector(transferSectorNumber,
68 *aligned_cast<SectorBuffer*>(buf));
69 ++transferSectorNumber;
70 return 512;
71 } catch (MSXException&) {
73 return 0;
74 }
75}
76
77void IDEHD::writeBlockComplete(AlignedBuffer& buf, unsigned count)
78{
79 try {
80 assert((count % 512) == 0);
81 size_t num = count / 512;
82 for (auto i : xrange(num)) {
83 writeSector(transferSectorNumber++,
84 *aligned_cast<SectorBuffer*>(buf + 512 * i));
85 }
86 } catch (MSXException&) {
88 }
89}
90
91void IDEHD::executeCommand(byte cmd)
92{
93 if (0x10 <= cmd && cmd < 0x20) {
94 // Recalibrate
95 setError(0);
96 setByteCount(0);
97 return;
98 }
99 switch (cmd) {
100 case 0x20: // Read Sector
101 case 0x21: // Read Sector without Retry
102 case 0x30: // Write Sector
103 case 0x31: { // Write Sector without Retry
104 unsigned sectorNumber = getSectorNumber();
105 unsigned numSectors = getNumSectors();
106 if ((sectorNumber + numSectors) > getNbSectors()) {
107 // Note: The original code set ABORT as well, but that is not
108 // allowed according to the spec.
109 setError(IDNF);
110 break;
111 }
112 transferSectorNumber = sectorNumber;
113 if (cmd < 0x30) {
114 startLongReadTransfer(numSectors * 512);
115 } else {
116 startWriteTransfer(numSectors * 512);
117 }
118 break;
119 }
120
121 case 0xF8: // Read Native Max Address
122 // We don't support the Host Protected Area feature set, but SymbOS
123 // uses only this particular command, so we support just this one.
124 // TODO this only supports 28-bit sector numbers
125 setSectorNumber(unsigned(getNbSectors()));
126 break;
127
128 default: // all others
130 }
131}
132
133
134template<typename Archive>
135void IDEHD::serialize(Archive& ar, unsigned /*version*/)
136{
137 // don't serialize SectorAccessibleDisk, DiskContainer base classes
138 ar.template serializeBase<HD>(*this);
139 ar.template serializeBase<AbstractIDEDevice>(*this);
140 ar.serialize("transferSectorNumber", transferSectorNumber);
141}
144
145} // 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:26
void serialize(Archive &ar, unsigned version)
Definition IDEHD.cc:135
IDEHD(const DeviceConfig &config)
Definition IDEHD.cc:16
std::string_view getMachineID() const
void readSector(size_t sector, SectorBuffer &buf) const
void writeSector(size_t sector, const SectorBuffer &buf)
void writeL32(void *p, uint32_t x)
Definition endian.hh:152
void writeL16(void *p, uint16_t x)
Definition endian.hh:144
This file implemented 3 utility functions:
Definition Autofire.cc:11
auto count(InputRange &&range, const T &value)
Definition ranges.hh:349
#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