OXYGENHUB
◆ main openmsx / src / fdc / Disk.cc
4.1 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "Disk.hh"
2
3
#include "DiskExceptions.hh"
4
5
#include "narrow.hh"
6
#include "one_of.hh"
7
8
namespace openmsx {
9
10
Disk::Disk(DiskName name_)
11
	: name(std::move(name_))
12
{
13
}
14
15
void Disk::writeTrack(uint8_t track, uint8_t side, const RawTrack& input)
16
{
17
	if (isWriteProtected()) {
18
		throw WriteProtectedException();
19
	}
20
	writeTrackImpl(track, side, input);
21
	flushCaches();
22
}
23
24
bool Disk::isDoubleSided()
25
{
26
	if (!nbSides) {
27
		detectGeometry();
28
	}
29
	return nbSides == 2;
30
}
31
32
// Note: Special case to convert between logical/physical sector numbers
33
//       for the boot sector and the 1st FAT sector (logical sector: 0/1,
34
//       physical location: track 0, side 0, sector 1/2): perform this
35
//       conversion without relying on the detected geometry parameters.
36
//       Otherwise the detectGeometry() method (which itself reads these
37
//       two sectors) would get in an infinite loop.
38
size_t Disk::physToLog(uint8_t track, uint8_t side, uint8_t sector)
39
{
40
	if ((track == 0) && (side == 0)) {
41
		return sector - 1;
42
	}
43
	if (!nbSides) {
44
		detectGeometry();
45
	}
46
	return sectorsPerTrack * (side + nbSides * track) + (sector - 1);
47
}
48
Disk::TSS Disk::logToPhys(size_t log)
49
{
50
	if (log <= 1) {
51
		uint8_t track = 0;
52
		uint8_t side = 0;
53
		auto sector = narrow<uint8_t>(log + 1);
54
		return {.track = track, .side = side, .sector = sector};
55
	}
56
	if (!nbSides) {
57
		detectGeometry();
58
	}
59
	auto track  = narrow<uint8_t>(log / (size_t(nbSides) * sectorsPerTrack));
60
	auto side   = narrow<uint8_t>((log / sectorsPerTrack) % nbSides);
61
	auto sector = narrow<uint8_t>((log % sectorsPerTrack) + 1);
62
	return {.track = track, .side = side, .sector = sector};
63
}
64
65
unsigned Disk::getSectorsPerTrack()
66
{
67
	if (!nbSides) {
68
		detectGeometry();
69
	}
70
	return sectorsPerTrack;
71
}
72
73
void Disk::detectGeometryFallback() // if all else fails, use statistics
74
{
75
	// TODO maybe also check for 8*80 for 8 sectors per track
76
	sectorsPerTrack = 9; // most of the time (sorry 5.25" disk users...)
77
	// 360k disks are likely to be single sided:
78
	nbSides = (getNbSectors() == 720) ? 1 : 2;
79
}
80
81
void Disk::detectGeometry()
82
{
83
	// From the MSX Red Book (p265):
84
	//
85
	//  How to determine media types
86
	//
87
	//  a) Read the boot sector (track 0, sector 1) of the target drive
88
	//
89
	//  b) Check if the first byte is either 0E9H or 0EBH (the JMP
90
	//     instruction on 8086)
91
	//
92
	//  c) If step b) fails, the disk is a version prior to MS-DOS 2.0;
93
	//     therefore, use the first byte of the FAT passed from the caller
94
	//     and make sure it is between 0F8h and 0FFh.
95
	//
96
	//     If step c) is successful, use this as a media descriptor.
97
	//     If step c) fails, then this disk cannot be read.
98
	//
99
	//  d) If step b) succeeds, read bytes # 0B to # 1D. This is the
100
	//     DPB for MS-DOS, Version 2.0 and above. The DPB for MSX-DOS can
101
	//     be obtained as follows.
102
	//
103
	//     ....
104
	//     +18 +19  Sectors per track
105
	//     +1A +1B  Number of heads
106
	//     ...
107
108
	//
109
	//     Media Descriptor  0F8H  0F9H  0FAh  0FBH  0FCH  0FDH  0FEH  0FFH
110
	//       byte (FAT-ID)
111
	//     Sectors/track        9     9     8     8     9     9     8     8
112
	//     No. of sides         1     2     1     2     1     2     1     2
113
	//     Tracks/side         80    80    80    80    40    40    40    40
114
	//     ...
115
116
	try {
117
		SectorBuffer buf;
118
		readSector(0, buf); // boot sector
119
		if (buf.raw[0] == one_of(0xE9, 0xEB)) {
120
			// use values from boot sector
121
			sectorsPerTrack = buf.bootSector.sectorsTrack;
122
			nbSides         = buf.bootSector.nrSides;
123
			if ((sectorsPerTrack == 0) || (sectorsPerTrack > 255) ||
124
			    (nbSides         == 0) || (nbSides         > 255)) {
125
				// seems like bogus values, use defaults
126
				detectGeometryFallback();
127
			}
128
		} else {
129
			readSector(1, buf); // 1st fat sector
130
			auto mediaDescriptor = buf.raw[0];
131
			if (mediaDescriptor >= 0xF8) {
132
				sectorsPerTrack = (mediaDescriptor & 2) ? 8 : 9;
133
				nbSides         = (mediaDescriptor & 1) ? 2 : 1;
134
			} else {
135
				// invalid media descriptor, just assume it's a
136
				// normal DS or SS DD disk
137
				detectGeometryFallback();
138
			}
139
		}
140
	} catch (MSXException&) {
141
		// read error, assume it's a 3.5" DS or SS DD disk
142
		detectGeometryFallback();
143
	}
144
}
145
146
} // namespace openmsx