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