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