openMSX
SectorBasedDisk.cc
Go to the documentation of this file.
1 #include "SectorBasedDisk.hh"
2 #include "MSXException.hh"
3 #include <cassert>
4 
5 namespace openmsx {
6 
8  : Disk(name)
9  , nbSectors(size_t(-1)) // to detect misuse
10  , cachedTrackNum(-1)
11 {
12 }
13 
14 void SectorBasedDisk::writeTrackImpl(byte track, byte side, const RawTrack& input)
15 {
16  for (auto& s : input.decodeAll()) {
17  // Ignore 'track' and 'head' information
18  // Always assume sectorsize = 512 (so also ignore sizeCode).
19  // Ignore CRC value/errors of both address and data.
20  // Ignore sector type (deleted or not)
21  // Ignore sectors that are outside the range 1..sectorsPerTrack
22  if ((s.sector < 1) || (s.sector > getSectorsPerTrack())) continue;
23  SectorBuffer buf;
24  input.readBlock(s.dataIdx, 512, buf.raw);
25  auto logicalSector = physToLog(track, side, s.sector);
26  writeSector(logicalSector, buf);
27  // it's important to use writeSector() and not writeSectorImpl()
28  // because only the former flushes SHA1 cache
29  }
30 }
31 
32 void SectorBasedDisk::readTrack(byte track, byte side, RawTrack& output)
33 {
34  // Try to cache the last result of this method (the cache will be
35  // flushed on any write to the disk). This very simple cache mechanism
36  // will typically already have a very high hit-rate. For example during
37  // emulation of a WD2793 read sector, we also emulate the search for
38  // the correct sector. So the disk rotates from sector to sector, and
39  // each time we re-read the track data (because emutime has passed).
40  // Typically the software will also read several sectors from the same
41  // track before moving to the next.
42  checkCaches();
43  int num = track | (side << 8);
44  if (num == cachedTrackNum) {
45  output = cachedTrackData;
46  return;
47  }
48  cachedTrackNum = num;
49 
50  // This disk image only stores the actual sector data, not all the
51  // extra gap, sync and header information that is in reality stored
52  // in between the sectors. This function transforms the cooked sector
53  // data back into raw track data. It assumes a standard IBM double
54  // density, 9 sectors/track, 512 bytes/sector track layout.
55  //
56  // -- track --
57  // gap4a 80 x 0x4e
58  // sync 12 x 0x00
59  // index mark 3 x 0xc2(*)
60  // 1 x 0xfc
61  // gap1 50 x 0x4e
62  // 9 x [sector] 9 x [[658]]
63  // gap4b 182 x 0x4e
64  //
65  // -- sector --
66  // sync 12 x 0x00
67  // ID addr mark 3 x 0xa1(*)
68  // 1 x 0xfe
69  // C H R N 4 x [..]
70  // CRC 2 x [..]
71  // gap2 22 x 0x4e
72  // sync 12 x 0x00
73  // data mark 3 x 0xa1(*)
74  // 1 x 0xfb
75  // data 512 x [..] <-- actual sector data
76  // CRC 2 x [..]
77  // gap3 84 x 0x4e
78  //
79  // (*) Missing clock transitions in MFM encoding
80 
81  try {
82  output.clear(RawTrack::STANDARD_SIZE); // clear idam positions
83 
84  unsigned idx = 0;
85  for (int i = 0; i < 80; ++i) output.write(idx++, 0x4E); // gap4a
86  for (int i = 0; i < 12; ++i) output.write(idx++, 0x00); // sync
87  for (int i = 0; i < 3; ++i) output.write(idx++, 0xC2); // index mark (1)
88  for (int i = 0; i < 1; ++i) output.write(idx++, 0xFC); // (2)
89  for (int i = 0; i < 50; ++i) output.write(idx++, 0x4E); // gap1
90 
91  for (int j = 0; j < 9; ++j) {
92  for (int i = 0; i < 12; ++i) output.write(idx++, 0x00); // sync
93 
94  for (int i = 0; i < 3; ++i) output.write(idx++, 0xA1); // addr mark (1)
95  output.addIdam(idx);
96  for (int i = 0; i < 1; ++i) output.write(idx++, 0xFE); // (2)
97  output.write(idx++, track); // C: Cylinder number
98  output.write(idx++, side); // H: Head Address
99  output.write(idx++, j + 1); // R: Record
100  output.write(idx++, 0x02); // N: Number (length of sector: 512 = 128 << 2)
101  word addrCrc = output.calcCrc(idx - 8, 8);
102  output.write(idx++, addrCrc >> 8); // CRC (high byte)
103  output.write(idx++, addrCrc & 0xff); // (low byte)
104 
105  for (int i = 0; i < 22; ++i) output.write(idx++, 0x4E); // gap2
106  for (int i = 0; i < 12; ++i) output.write(idx++, 0x00); // sync
107 
108  for (int i = 0; i < 3; ++i) output.write(idx++, 0xA1); // data mark (1)
109  for (int i = 0; i < 1; ++i) output.write(idx++, 0xFB); // (2)
110 
111  auto logicalSector = physToLog(track, side, j + 1);
112  SectorBuffer buf;
113  readSector(logicalSector, buf);
114  for (int i = 0; i < 512; ++i) output.write(idx++, buf.raw[i]);
115 
116  word dataCrc = output.calcCrc(idx - (512 + 4), 512 + 4);
117  output.write(idx++, dataCrc >> 8); // CRC (high byte)
118  output.write(idx++, dataCrc & 0xff); // (low byte)
119 
120  for (int i = 0; i < 84; ++i) output.write(idx++, 0x4E); // gap3
121  }
122 
123  for (int i = 0; i < 182; ++i) output.write(idx++, 0x4E); // gap4b
124  assert(idx == RawTrack::STANDARD_SIZE);
125  } catch (MSXException& /*e*/) {
126  // There was an error while reading the actual sector data.
127  // Most likely this is because we're reading the 81th track on
128  // a disk with only 80 tracks (or similar). If you do this on a
129  // real disk, you simply read an 'empty' track. So we do the
130  // same here.
131  output.clear(RawTrack::STANDARD_SIZE);
132  cachedTrackNum = -1; // needed?
133  }
134  cachedTrackData = output;
135 }
136 
138 {
140  cachedTrackNum = -1;
141 }
142 
143 size_t SectorBasedDisk::getNbSectorsImpl() const
144 {
145  assert(nbSectors != size_t(-1)); // must have been initialized
146  return nbSectors;
147 }
149 {
150  assert(nbSectors == size_t(-1)); // can only set this once
151  nbSectors = num;
152 }
153 
155 {
156  // the following are just heuristics...
157 
158  if (getNbSectors() == 1440) {
159  // explicitly check for 720kb filesize
160 
161  // "trojka.dsk" is 720kb, but has bootsector and FAT media ID
162  // for a single sided disk. From an emulator point of view it
163  // must be accessed as a double sided disk.
164 
165  // "SDSNAT2.DSK" has invalid media ID in both FAT and
166  // bootsector, other data in the bootsector is invalid as well.
167  // Altough the first byte of the bootsector is 0xE9 to indicate
168  // valid bootsector data. The only way to detect the format is
169  // to look at the diskimage filesize.
170 
172  setNbSides(2);
173 
174  } else {
175  // Don't check for "360kb -> single sided disk". The MSXMania
176  // disks are double sided disk but are truncated at 360kb.
178  }
179 }
180 
181 } // namespace openmsx
void readSector(size_t sector, SectorBuffer &buf)
void setSectorsPerTrack(unsigned num)
Definition: Disk.hh:35
std::vector< Sector > decodeAll() const
Get info on all sectors in this track.
Definition: RawTrack.cc:101
unsigned char byte
8 bit unsigned integer
Definition: openmsx.hh:27
size_t physToLog(byte track, byte side, byte sector)
Definition: Disk.cc:36
SectorBasedDisk(const DiskName &name)
void detectGeometry() override
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:7
unsigned short word
16 bit unsigned integer
Definition: openmsx.hh:32
void setNbSides(unsigned num)
Definition: Disk.hh:37
static const unsigned STANDARD_SIZE
Definition: RawTrack.hh:71
void readBlock(int idx, unsigned size, byte *destination) const
Like memcpy() but copy from/to circular buffer.
Definition: RawTrack.cc:144
unsigned getSectorsPerTrack()
Definition: Disk.cc:62
virtual void detectGeometry()
Definition: Disk.cc:78
void setNbSectors(size_t num)
void writeSector(size_t sector, const SectorBuffer &buf)