openMSX
SectorBasedDisk.cc
Go to the documentation of this file.
1#include "SectorBasedDisk.hh"
2#include "MSXException.hh"
3#include "narrow.hh"
4#include "xrange.hh"
5#include <cassert>
6
7namespace openmsx {
8
10 : Disk(std::move(name_))
11{
12}
13
14void SectorBasedDisk::writeTrackImpl(uint8_t track, uint8_t side, const RawTrack& input)
15{
16 for (auto& s : input.decodeAll()) {
17 // Ignore 'track' and 'head' information
18 // Always assume sector-size = 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, 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
32void SectorBasedDisk::readTrack(uint8_t track, uint8_t 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.
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 int idx = 0;
85 auto write = [&](unsigned n, uint8_t value) {
86 repeat(n, [&] { output.write(idx++, value); });
87 };
88
89 write(80, 0x4E); // gap4a
90 write(12, 0x00); // sync
91 write( 3, 0xC2); // index mark (1)
92 write( 1, 0xFC); // (2)
93 write(50, 0x4E); // gap1
94
95 for (auto j : xrange(9)) {
96 write(12, 0x00); // sync
97
98 write( 3, 0xA1); // addr mark (1)
99 output.write(idx++, 0xFE, true); // (2) add idam
100 output.write(idx++, track); // C: Cylinder number
101 output.write(idx++, side); // H: Head Address
102 output.write(idx++, narrow<uint8_t>(j + 1)); // R: Record
103 output.write(idx++, 0x02); // N: Number (length of sector: 512 = 128 << 2)
104 uint16_t addrCrc = output.calcCrc(idx - 8, 8);
105 output.write(idx++, narrow_cast<uint8_t>(addrCrc >> 8)); // CRC (high byte)
106 output.write(idx++, narrow_cast<uint8_t>(addrCrc & 0xff)); // (low byte)
107
108 write(22, 0x4E); // gap2
109 write(12, 0x00); // sync
110
111 write( 3, 0xA1); // data mark (1)
112 write( 1, 0xFB); // (2)
113
114 auto logicalSector = physToLog(track, side, narrow<uint8_t>(j + 1));
115 SectorBuffer buf;
116 readSector(logicalSector, buf);
117 for (auto& r : buf.raw) output.write(idx++, r);
118
119 uint16_t dataCrc = output.calcCrc(idx - (512 + 4), 512 + 4);
120 output.write(idx++, narrow_cast<uint8_t>(dataCrc >> 8)); // CRC (high byte)
121 output.write(idx++, narrow_cast<uint8_t>(dataCrc & 0xff)); // (low byte)
122
123 write(84, 0x4E); // gap3
124 }
125
126 write(182, 0x4E); // gap4b
127 assert(idx == RawTrack::STANDARD_SIZE);
128 } catch (MSXException& /*e*/) {
129 // There was an error while reading the actual sector data.
130 // Most likely this is because we're reading the 81th track on
131 // a disk with only 80 tracks (or similar). If you do this on a
132 // real disk, you simply read an 'empty' track. So we do the
133 // same here.
134 output.clear(RawTrack::STANDARD_SIZE);
135 cachedTrackNum = -1; // needed?
136 }
137 cachedTrackData = output;
138}
139
141{
143 cachedTrackNum = -1;
144}
145
146size_t SectorBasedDisk::getNbSectorsImpl() const
147{
148 assert(nbSectors != size_t(-1)); // must have been initialized
149 return nbSectors;
150}
152{
153 assert(nbSectors == size_t(-1)); // can only set this once
154 nbSectors = num;
155}
156
158{
159 // the following are just heuristics...
160
161 if (getNbSectors() == 1440) {
162 // explicitly check for 720kb filesize
163
164 // "trojka.dsk" is 720kb, but has boot sector and FAT media ID
165 // for a single sided disk. From an emulator point of view it
166 // must be accessed as a double sided disk.
167
168 // "SDSNAT2.DSK" has invalid media ID in both FAT and
169 // boot sector, other data in the boot sector is invalid as well.
170 // Although the first byte of the boot sector is 0xE9 to indicate
171 // valid boot sector data. The only way to detect the format is
172 // to look at the disk image filesize.
173
175 setNbSides(2);
176
177 } else {
178 // Don't check for "360kb -> single sided disk". The MSXMania
179 // disks are double sided disk but are truncated at 360kb.
181 }
182}
183
184} // namespace openmsx
size_t physToLog(uint8_t track, uint8_t side, uint8_t sector)
Definition Disk.cc:36
unsigned getSectorsPerTrack()
Definition Disk.cc:63
void setNbSides(unsigned num)
Definition Disk.hh:44
void setSectorsPerTrack(unsigned num)
Definition Disk.hh:42
virtual void detectGeometry()
Definition Disk.cc:79
static constexpr unsigned STANDARD_SIZE
Definition RawTrack.hh:74
void readBlock(int idx, std::span< uint8_t > destination) const
Like memcpy() but copy from/to circular buffer.
Definition RawTrack.cc:157
void readSector(size_t sector, SectorBuffer &buf)
void writeSector(size_t sector, const SectorBuffer &buf)
void setNbSectors(size_t num)
SectorBasedDisk(DiskName name)
This file implemented 3 utility functions:
Definition Autofire.cc:9
STL namespace.
std::array< uint8_t, 512 > raw
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
Definition xrange.hh:147
constexpr auto xrange(T e)
Definition xrange.hh:132