OXYGENHUB
◆ main openmsx / src / fdc / TurboRFDC.cc
6.6 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
/*
2
 * Based on code from NLMSX written by Frits Hilderink
3
 *
4
 * Despite the name, the class implements MSX connection to TC8566AF FDC for
5
 * any MSX that uses it. To make reuse possible, it implements two sets of
6
 * I/O configurations, one is used on turboR, the other on other machines with
7
 * this FDC.
8
 * There's also a mapper mechanism implemented. It's only used on the turboR,
9
 * but all other machines only have a 16kB diskROM, so it's practically
10
 * ineffective there. The mapper has 0x7FF0 as switch address. (Thanks to
11
 * NYYRIKKI for this info.)
12
 */
13
14
#include "TurboRFDC.hh"
15
16
#include "CacheLine.hh"
17
#include "MSXCPU.hh"
18
#include "MSXException.hh"
19
#include "Rom.hh"
20
#include "serialize.hh"
21
22
#include "narrow.hh"
23
24
namespace openmsx {
25
26
[[nodiscard]] static TurboRFDC::Type parseType(const DeviceConfig& config)
27
{
28
	auto ioRegs = config.getChildData("io_regs", {});
29
	if (ioRegs == "7FF2") {
30
		return TurboRFDC::Type::R7FF2;
31
	} else if (ioRegs == "7FF8") {
32
		return TurboRFDC::Type::R7FF8;
33
	} else if (ioRegs.empty()) {
34
		// for backwards compatibility
35
		return TurboRFDC::Type::BOTH;
36
	} else {
37
		throw MSXException(
38
			"Invalid 'io_regs' specification: expected one of "
39
			"'7FF2' or '7FF8', but got: ", ioRegs);
40
	}
41
}
42
43
TurboRFDC::TurboRFDC(DeviceConfig& config)
44
	: MSXFDC(config)
45
	, controller(getScheduler(), drives, getCliComm(), getCurrentTime())
46
	, romBlockDebug(*this, std::span{&bank, 1}, 0x4000, 0x4000, 14)
47
	, memory(subspan<0x4000>(*rom))
48
	, blockMask(narrow_cast<byte>((rom->size() / 0x4000) - 1))
49
	, type(parseType(config))
50
{
51
	auto size = rom->size();
52
	if ((size % 0x4000) != 0) {
53
		throw MSXException("TurboRFDC rom size must be a multiple of 16kB");
54
	}
55
	if (size == 0) {
56
		throw MSXException("TurboRFDC rom size too small");
57
	}
58
	if ((size / 0x4000) > 256) {
59
		throw MSXException("TurboRFDC rom size too large");
60
	}
61
	reset(getCurrentTime());
62
}
63
64
void TurboRFDC::reset(EmuTime time)
65
{
66
	setBank(0);
67
	controller.reset(time);
68
}
69
70
byte TurboRFDC::readMem(uint16_t address, EmuTime time_)
71
{
72
	EmuTime time = time_;
73
	if (0x3FF0 <= (address & 0x3FFF)) {
74
		// Reading or writing to this region takes 1 extra clock
75
		// cycle. But only in R800 mode. Verified on a real turboR
76
		// machine, it happens for all 16 positions in this region
77
		// and both for reading and writing.
78
		time = getCPU().waitCyclesR800(time, 1);
79
		if (type != Type::R7FF8) { // turboR or BOTH
80
			switch (address & 0xF) {
81
			case 0x1: {
82
				byte result = 0x33;
83
				if (controller.diskChanged(0)) result &= ~0x10;
84
				if (controller.diskChanged(1)) result &= ~0x20;
85
				return result;
86
			}
87
			case 0x4: return controller.readStatus(time);
88
			case 0x5: return controller.readDataPort(time);
89
			}
90
		}
91
		if (type != Type::R7FF2) { // non-turboR or BOTH
92
			switch (address & 0xF) {
93
			case 0xA: return controller.readStatus(time);
94
			case 0xB: return controller.readDataPort(time);
95
			}
96
97
		}
98
	}
99
	// all other stuff is handled by peekMem()
100
	return TurboRFDC::peekMem(address, time);
101
}
102
103
104
byte TurboRFDC::peekMem(uint16_t address, EmuTime time) const
105
{
106
	if (0x3FF0 <= (address & 0x3FFF)) {
107
		// note: this implementation requires that the handled
108
		//    addresses for the MSX2 and TURBOR variants don't overlap
109
		if (type != Type::R7FF8) { // turboR or BOTH
110
			switch (address & 0xF) {
111
			case 0x0: return bank;
112
			case 0x1: {
113
				// bit 0  FD2HD1  High Density detect drive 1
114
				// bit 1  FD2HD2  High Density detect drive 2
115
				// bit 4  FDCHG1  Disk Change detect on drive 1
116
				// bit 5  FDCHG2  Disk Change detect on drive 2
117
				// active low
118
				byte result = 0x33;
119
				if (controller.peekDiskChanged(0)) result &= ~0x10;
120
				if (controller.peekDiskChanged(1)) result &= ~0x20;
121
				return result;
122
			}
123
			case 0x4: return controller.peekStatus();
124
			case 0x5: return controller.peekDataPort(time);
125
			}
126
		}
127
		if (type != Type::R7FF2) { // non-turboR or BOTH
128
			switch (address & 0xF) {
129
			case 0xA: return controller.peekStatus();
130
			case 0xB: return controller.peekDataPort(time);
131
			}
132
		}
133
		switch (address & 0xF) {
134
		// TODO Any idea what these 4 are? I've confirmed that on a
135
		// real FS-A1GT I get these values, though the ROM dumps
136
		// contain 0xFF in these locations. When looking at the ROM
137
		// content via the 'RomPanasonic' mapper in slot 3-3, you can
138
		// see that the ROM dumps are correct (these 4 values are not
139
		// part of the ROM).
140
		// This MRC post indicates that also on non-turbor machines
141
		// you see these 4 values (bluemsx' post of 10-03-2013, 10:15):
142
		//   http://www.msx.org/forum/msx-talk/development/finally-have-feature-request-openmsx?page=3
143
		case 0xC: return 0xFC;
144
		case 0xD: return 0xFC;
145
		case 0xE: return 0xFF;
146
		case 0xF: return 0x3F;
147
		default:  return 0xFF; // other regs in this region
148
		}
149
	} else if ((0x4000 <= address) && (address < 0x8000)) {
150
		return memory[address & 0x3FFF];
151
	} else {
152
		return 0xFF;
153
	}
154
}
155
156
const byte* TurboRFDC::getReadCacheLine(uint16_t start) const
157
{
158
	if ((start & 0x3FF0) == (0x3FF0 & CacheLine::HIGH)) {
159
		return nullptr;
160
	} else if ((0x4000 <= start) && (start < 0x8000)) {
161
		return &memory[start & 0x3FFF];
162
	} else {
163
		return unmappedRead.data();
164
	}
165
}
166
167
void TurboRFDC::writeMem(uint16_t address, byte value, EmuTime time_)
168
{
169
	EmuTime time = time_;
170
	if (0x3FF0 <= (address & 0x3FFF)) {
171
		// See comment in readMem().
172
		time = getCPU().waitCyclesR800(time, 1);
173
	}
174
	if (address == 0x7FF0) {
175
		setBank(value);
176
	} else {
177
		if (type != Type::R7FF8) { // turboR or BOTH
178
			switch (address & 0x3FFF) {
179
			case 0x3FF2:
180
				controller.writeControlReg0(value, time);
181
				break;
182
			case 0x3ff3:
183
				controller.writeControlReg1(value, time);
184
				break;
185
			case 0x3FF5:
186
				controller.writeDataPort(value, time);
187
				break;
188
			}
189
		}
190
		if (type != Type::R7FF2) { // non-turboR or BOTH
191
			switch (address & 0x3FFF) {
192
			case 0x3FF8:
193
				controller.writeControlReg0(value, time);
194
				break;
195
			case 0x3FF9:
196
				controller.writeControlReg1(value, time);
197
				break;
198
			case 0x3FFB:
199
				controller.writeDataPort(value, time);
200
				break;
201
			}
202
		}
203
	}
204
}
205
206
void TurboRFDC::setBank(byte value)
207
{
208
	invalidateDeviceRCache(0x4000, 0x4000);
209
	bank = value & blockMask;
210
	memory = subspan<0x4000>(*rom, 0x4000 * bank);
211
}
212
213
byte* TurboRFDC::getWriteCacheLine(uint16_t address)
214
{
215
	if ((address == (0x7FF0 & CacheLine::HIGH)) ||
216
	    ((address & 0x3FF0) == (0x3FF0 & CacheLine::HIGH))) {
217
		return nullptr;
218
	} else {
219
		return unmappedWrite.data();
220
	}
221
}
222
223
224
template<typename Archive>
225
void TurboRFDC::serialize(Archive& ar, unsigned /*version*/)
226
{
227
	ar.template serializeBase<MSXFDC>(*this);
228
	ar.serialize("TC8566AF", controller,
229
	             "bank",     bank);
230
	if constexpr (Archive::IS_LOADER) {
231
		setBank(bank);
232
	}
233
}
234
INSTANTIATE_SERIALIZE_METHODS(TurboRFDC);
235
REGISTER_MSXDEVICE(TurboRFDC, "TurboRFDC");
236
237
} // namespace openmsx