OXYGENHUB
◆ main openmsx / src / fdc / MSXFDC.cc
4.1 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "MSXFDC.hh"
2
3
#include "RealDrive.hh"
4
5
#include "CacheLine.hh"
6
#include "ConfigException.hh"
7
#include "MSXException.hh"
8
#include "XMLElement.hh"
9
#include "serialize.hh"
10
11
#include "enumerate.hh"
12
#include "narrow.hh"
13
14
#include <array>
15
#include <memory>
16
17
namespace openmsx {
18
19
MSXFDC::MSXFDC(DeviceConfig& config, const std::string& romId, bool needROM,
20
               DiskDrive::TrackMode trackMode)
21
	: MSXDevice(config)
22
	, rom(needROM
23
		? std::optional<Rom>(std::in_place, getName() + " ROM", "rom", config, romId)
24
		: std::nullopt) // e.g. Spectravideo_SVI-328 doesn't have a disk rom
25
{
26
	if (needROM && (rom->size() == 0)) {
27
		throw MSXException(
28
			"Empty ROM not allowed for \"", getName(), "\".");
29
	}
30
	bool singleSided = config.findChild("singlesided") != nullptr;
31
	int numDrives = config.getChildDataAsInt("drives", 1);
32
	if ((0 > numDrives) || (numDrives >= 4)) {
33
		throw MSXException("Invalid number of drives: ", numDrives);
34
	}
35
	// Nominal number of tracks per side, this is not a hard limit on how
36
	// far the head can seek. Only used to select the dir-as-disk geometry:
37
	// 80 -> 360kB/720kB (3.5"), 40 -> 180kB/360kB (5.25").
38
	int nominalTracks = config.getChildDataAsInt("nominal_tracks", 80);
39
	if ((nominalTracks != 40) && (nominalTracks != 80)) {
40
		throw MSXException("Invalid nominal number of tracks: ", nominalTracks);
41
	}
42
	unsigned timeout = config.getChildDataAsInt("motor_off_timeout_ms", 0);
43
	const auto* styleEl = config.findChild("connectionstyle");
44
	bool signalsNeedMotorOn = !styleEl || (styleEl->getData() == "Philips");
45
	EmuDuration motorTimeout = EmuDuration::msec(timeout);
46
	int i = 0;
47
	for (/**/; i < numDrives; ++i) {
48
		drives[i] = std::make_unique<RealDrive>(
49
			getMotherBoard(), motorTimeout, signalsNeedMotorOn,
50
			!singleSided, narrow<unsigned>(nominalTracks), trackMode);
51
	}
52
	for (/**/; i < 4; ++i) {
53
		drives[i] = std::make_unique<DummyDrive>();
54
	}
55
}
56
57
void MSXFDC::powerDown(EmuTime time)
58
{
59
	for (const auto& drive : drives) {
60
		drive->setMotor(false, time);
61
	}
62
}
63
64
byte MSXFDC::readMem(uint16_t address, EmuTime /*time*/)
65
{
66
	return *MSXFDC::getReadCacheLine(address);
67
}
68
69
byte MSXFDC::peekMem(uint16_t address, EmuTime /*time*/) const
70
{
71
	return *MSXFDC::getReadCacheLine(address);
72
}
73
74
const byte* MSXFDC::getReadCacheLine(uint16_t start) const
75
{
76
	assert(rom);
77
	if (romVisibilityStart <= start && start <= romVisibilityLast) {
78
		return &(*rom)[start & 0x3FFF];
79
	}
80
	return unmappedRead.data();
81
}
82
83
void MSXFDC::getExtraDeviceInfo(TclObject& result) const
84
{
85
	if (rom) {
86
		rom->getInfo(result);
87
	}
88
}
89
90
void MSXFDC::parseRomVisibility(DeviceConfig& config, unsigned defaultBase, unsigned defaultSize)
91
{
92
	auto base = defaultBase;
93
	auto size = defaultSize;
94
	if (const auto* visibility = config.findChild("rom_visibility")) {
95
		base = visibility->getAttributeValueAsInt("base", base);
96
		size = visibility->getAttributeValueAsInt("size", size);
97
	}
98
99
	if (base & CacheLine::LOW) throw ConfigException("rom_visibility base must be a multiple of 0x100.");
100
	if (size & CacheLine::LOW) throw ConfigException("rom_visibility size must be a multiple of 0x100.");
101
	if (base >= 0x10000) throw ConfigException("rom_visibility base must be between 0 and 0xFFFF.");
102
	if (size < 1 || size > 0x10000) throw ConfigException("rom_visibility size must be between 1 and 0x10000.");
103
	if (base + size > 0x10000) throw ConfigException("rom_visibility base + size must be between 1 and 0x10000.");
104
105
	romVisibilityStart = narrow_cast<uint16_t>(base);
106
	romVisibilityLast = narrow_cast<uint16_t>(base + size - 1);
107
}
108
109
110
template<typename Archive>
111
void MSXFDC::serialize(Archive& ar, unsigned /*version*/)
112
{
113
	ar.template serializeBase<MSXDevice>(*this);
114
115
	// Drives are already constructed at this point, so we cannot use the
116
	// polymorphic object construction of the serialization framework.
117
	// Destroying and reconstructing the drives is not an option because
118
	// DriveMultiplexer already has pointers to the drives.
119
	std::array<char, 7> tag = {'d', 'r', 'i', 'v', 'e', 'X', 0};
120
	for (auto [i, drv] : enumerate(drives)) {
121
		if (auto* drive = dynamic_cast<RealDrive*>(drv.get())) {
122
			tag[5] = char('a' + i);
123
			ar.serialize(tag.data(), *drive);
124
		}
125
	}
126
}
127
INSTANTIATE_SERIALIZE_METHODS(MSXFDC);
128
129
} // namespace openmsx