OXYGENHUB
◆ main openmsx / src / fdc / SectorBasedDisk.cc
5.8 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "SectorBasedDisk.hh"
2
3
#include "MSXException.hh"
4
5
#include "narrow.hh"
6
#include "xrange.hh"
7
8
#include <cassert>
9
10
namespace openmsx {
11
12
SectorBasedDisk::SectorBasedDisk(DiskName name_)
13
	: Disk(std::move(name_))
14
{
15
}
16
17
void SectorBasedDisk::writeTrackImpl(uint8_t track, uint8_t side, const RawTrack& input)
18
{
19
	for (const auto& s : input.decodeAll()) {
20
		// Ignore 'track' and 'head' information
21
		// Always assume sector-size = 512 (so also ignore sizeCode).
22
		// Ignore CRC value/errors of both address and data.
23
		// Ignore sector type (deleted or not)
24
		// Ignore sectors that are outside the range 1..sectorsPerTrack
25
		if ((s.sector < 1) || (s.sector > getSectorsPerTrack())) continue;
26
		SectorBuffer buf;
27
		input.readBlock(s.dataIdx, buf.raw);
28
		auto logicalSector = physToLog(track, side, s.sector);
29
		writeSector(logicalSector, buf);
30
		// it's important to use writeSector() and not writeSectorImpl()
31
		// because only the former flushes SHA1 cache
32
	}
33
}
34
35
void SectorBasedDisk::readTrack(uint8_t track, uint8_t side, RawTrack& output)
36
{
37
	// Try to cache the last result of this method (the cache will be
38
	// flushed on any write to the disk). This very simple cache mechanism
39
	// will typically already have a very high hit-rate. For example during
40
	// emulation of a WD2793 read sector, we also emulate the search for
41
	// the correct sector. So the disk rotates from sector to sector, and
42
	// each time we re-read the track data (because EmuTime has passed).
43
	// Typically the software will also read several sectors from the same
44
	// track before moving to the next.
45
	checkCaches();
46
	int num = track | (side << 8);
47
	if (num == cachedTrackNum) {
48
		output = cachedTrackData;
49
		return;
50
	}
51
	cachedTrackNum = num;
52
53
	// This disk image only stores the actual sector data, not all the
54
	// extra gap, sync and header information that is in reality stored
55
	// in between the sectors. This function transforms the cooked sector
56
	// data back into raw track data. It assumes a standard IBM double
57
	// density, 9 sectors/track, 512 bytes/sector track layout.
58
	//
59
	// -- track --
60
	// gap4a         80 x 0x4e
61
	// sync          12 x 0x00
62
	// index mark     3 x 0xc2(*)
63
	//                1 x 0xfc
64
	// gap1          50 x 0x4e
65
	// 9 x [sector]   9 x [[658]]
66
	// gap4b        182 x 0x4e
67
	//
68
	// -- sector --
69
	// sync          12 x 0x00
70
	// ID addr mark   3 x 0xa1(*)
71
	//                1 x 0xfe
72
	// C H R N        4 x [..]
73
	// CRC            2 x [..]
74
	// gap2          22 x 0x4e
75
	// sync          12 x 0x00
76
	// data mark      3 x 0xa1(*)
77
	//                1 x 0xfb
78
	// data         512 x [..]    <-- actual sector data
79
	// CRC            2 x [..]
80
	// gap3          84 x 0x4e
81
	//
82
	// (*) Missing clock transitions in MFM encoding
83
84
	try {
85
		output.clear(RawTrack::STANDARD_SIZE); // clear idam positions
86
87
		int idx = 0;
88
		auto write = [&](unsigned n, uint8_t value) {
89
			repeat(n, [&] { output.write(idx++, value); });
90
		};
91
92
		write(80, 0x4E); // gap4a
93
		write(12, 0x00); // sync
94
		write( 3, 0xC2); // index mark (1)
95
		write( 1, 0xFC); //            (2)
96
		write(50, 0x4E); // gap1
97
98
		for (auto j : xrange(9)) {
99
			write(12, 0x00); // sync
100
101
			write( 3, 0xA1);                 // addr mark (1)
102
			output.write(idx++, 0xFE, true); //           (2) add idam
103
			output.write(idx++, track); // C: Cylinder number
104
			output.write(idx++, side);  // H: Head Address
105
			output.write(idx++, narrow<uint8_t>(j + 1)); // R: Record
106
			output.write(idx++, 0x02);  // N: Number (length of sector: 512 = 128 << 2)
107
			uint16_t addrCrc = output.calcCrc(idx - 8, 8);
108
			output.write(idx++, narrow_cast<uint8_t>(addrCrc >> 8));   // CRC (high byte)
109
			output.write(idx++, narrow_cast<uint8_t>(addrCrc & 0xff)); //     (low  byte)
110
111
			write(22, 0x4E); // gap2
112
			write(12, 0x00); // sync
113
114
			write( 3, 0xA1); // data mark (1)
115
			write( 1, 0xFB); //           (2)
116
117
			auto logicalSector = physToLog(track, side, narrow<uint8_t>(j + 1));
118
			SectorBuffer buf;
119
			readSector(logicalSector, buf);
120
			for (auto r : buf.raw) output.write(idx++, r);
121
122
			uint16_t dataCrc = output.calcCrc(idx - (512 + 4), 512 + 4);
123
			output.write(idx++, narrow_cast<uint8_t>(dataCrc >> 8));   // CRC (high byte)
124
			output.write(idx++, narrow_cast<uint8_t>(dataCrc & 0xff)); //     (low  byte)
125
126
			write(84, 0x4E); // gap3
127
		}
128
129
		write(182, 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
143
void SectorBasedDisk::flushCaches()
144
{
145
	Disk::flushCaches();
146
	cachedTrackNum = -1;
147
}
148
149
size_t SectorBasedDisk::getNbSectorsImpl()
150
{
151
	assert(nbSectors != size_t(-1)); // must have been initialized
152
	return nbSectors;
153
}
154
void SectorBasedDisk::setNbSectors(size_t num)
155
{
156
	assert(nbSectors == size_t(-1)); // can only set this once
157
	nbSectors = num;
158
}
159
160
void SectorBasedDisk::detectGeometry()
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 boot sector 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
		// boot sector, other data in the boot sector is invalid as well.
173
		// Although the first byte of the boot sector is 0xE9 to indicate
174
		// valid boot sector data. The only way to detect the format is
175
		// to look at the disk image filesize.
176
177
		setSectorsPerTrack(9);
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.
183
		Disk::detectGeometry();
184
	}
185
}
186
187
} // namespace openmsx