openMSX
SectorBasedDisk.cc
Go to the documentation of this file.
1#include "SectorBasedDisk.hh"
2#include "MSXException.hh"
3#include "xrange.hh"
4#include <cassert>
5
6namespace openmsx {
7
9 : Disk(std::move(name_))
10 , nbSectors(size_t(-1)) // to detect misuse
11 , cachedTrackNum(-1)
12{
13}
14
15void SectorBasedDisk::writeTrackImpl(byte track, byte side, const RawTrack& input)
16{
17 for (auto& s : input.decodeAll()) {
18 // Ignore 'track' and 'head' information
19 // Always assume sectorsize = 512 (so also ignore sizeCode).
20 // Ignore CRC value/errors of both address and data.
21 // Ignore sector type (deleted or not)
22 // Ignore sectors that are outside the range 1..sectorsPerTrack
23 if ((s.sector < 1) || (s.sector > getSectorsPerTrack())) continue;
24 SectorBuffer buf;
25 input.readBlock(s.dataIdx, buf.raw);
26 auto logicalSector = physToLog(track, side, s.sector);
27 writeSector(logicalSector, buf);
28 // it's important to use writeSector() and not writeSectorImpl()
29 // because only the former flushes SHA1 cache
30 }
31}
32
33void SectorBasedDisk::readTrack(byte track, byte side, RawTrack& output)
34{
35 // Try to cache the last result of this method (the cache will be
36 // flushed on any write to the disk). This very simple cache mechanism
37 // will typically already have a very high hit-rate. For example during
38 // emulation of a WD2793 read sector, we also emulate the search for
39 // the correct sector. So the disk rotates from sector to sector, and
40 // each time we re-read the track data (because emutime has passed).
41 // Typically the software will also read several sectors from the same
42 // track before moving to the next.
44 int num = track | (side << 8);
45 if (num == cachedTrackNum) {
46 output = cachedTrackData;
47 return;
48 }
49 cachedTrackNum = num;
50
51 // This disk image only stores the actual sector data, not all the
52 // extra gap, sync and header information that is in reality stored
53 // in between the sectors. This function transforms the cooked sector
54 // data back into raw track data. It assumes a standard IBM double
55 // density, 9 sectors/track, 512 bytes/sector track layout.
56 //
57 // -- track --
58 // gap4a 80 x 0x4e
59 // sync 12 x 0x00
60 // index mark 3 x 0xc2(*)
61 // 1 x 0xfc
62 // gap1 50 x 0x4e
63 // 9 x [sector] 9 x [[658]]
64 // gap4b 182 x 0x4e
65 //
66 // -- sector --
67 // sync 12 x 0x00
68 // ID addr mark 3 x 0xa1(*)
69 // 1 x 0xfe
70 // C H R N 4 x [..]
71 // CRC 2 x [..]
72 // gap2 22 x 0x4e
73 // sync 12 x 0x00
74 // data mark 3 x 0xa1(*)
75 // 1 x 0xfb
76 // data 512 x [..] <-- actual sector data
77 // CRC 2 x [..]
78 // gap3 84 x 0x4e
79 //
80 // (*) Missing clock transitions in MFM encoding
81
82 try {
83 output.clear(RawTrack::STANDARD_SIZE); // clear idam positions
84
85 unsigned idx = 0;
86 auto write = [&](unsigned n, byte value) {
87 repeat(n, [&] { output.write(idx++, value); });
88 };
89
90 write(80, 0x4E); // gap4a
91 write(12, 0x00); // sync
92 write( 3, 0xC2); // index mark (1)
93 write( 1, 0xFC); // (2)
94 write(50, 0x4E); // gap1
95
96 for (auto j : xrange(9)) {
97 write(12, 0x00); // sync
98
99 write( 3, 0xA1); // addr mark (1)
100 output.write(idx++, 0xFE, true); // (2) add idam
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 write(22, 0x4E); // gap2
110 write(12, 0x00); // sync
111
112 write( 3, 0xA1); // data mark (1)
113 write( 1, 0xFB); // (2)
114
115 auto logicalSector = physToLog(track, side, j + 1);
116 SectorBuffer buf;
117 readSector(logicalSector, buf);
118 for (auto& r : buf.raw) output.write(idx++, r);
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 write(84, 0x4E); // gap3
125 }
126
127 write(182, 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
147size_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
unsigned getSectorsPerTrack()
Definition: Disk.cc:62
void setNbSides(unsigned num)
Definition: Disk.hh:44
void setSectorsPerTrack(unsigned num)
Definition: Disk.hh:42
virtual void detectGeometry()
Definition: Disk.cc:78
size_t physToLog(byte track, byte side, byte sector)
Definition: Disk.cc:35
void readBlock(int idx, std::span< byte > destination) const
Like memcpy() but copy from/to circular buffer.
Definition: RawTrack.cc:157
std::vector< Sector > decodeAll() const
Get info on all sectors in this track.
Definition: RawTrack.cc:110
static constexpr unsigned STANDARD_SIZE
Definition: RawTrack.hh:73
void readSector(size_t sector, SectorBuffer &buf)
void writeSector(size_t sector, const SectorBuffer &buf)
void detectGeometry() override
void setNbSectors(size_t num)
SectorBasedDisk(DiskName name)
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
STL namespace.
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
Definition: xrange.hh:148
constexpr auto xrange(T e)
Definition: xrange.hh:133