OXYGENHUB
◆ main openmsx / src / sound / Y8950Periphery.cc
8.8 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "Y8950Periphery.hh"
2
3
#include "MSXAudio.hh"
4
#include "Y8950.hh"
5
6
#include "BooleanSetting.hh"
7
#include "CacheLine.hh"
8
#include "DeviceConfig.hh"
9
#include "MSXCPU.hh"
10
#include "MSXCPUInterface.hh"
11
#include "MSXDevice.hh"
12
#include "MSXException.hh"
13
#include "Ram.hh"
14
#include "Rom.hh"
15
#include "serialize.hh"
16
17
#include "StringOp.hh"
18
19
#include <memory>
20
#include <string>
21
22
namespace openmsx {
23
24
// Subclass declarations:
25
26
class MusicModulePeriphery final : public Y8950Periphery
27
{
28
public:
29
	explicit MusicModulePeriphery(MSXAudio& audio);
30
	void write(uint4_t outputs, uint4_t values, EmuTime time) override;
31
	[[nodiscard]] uint4_t read(EmuTime time) override;
32
33
	template<typename Archive>
34
	void serialize(Archive& /*ar*/, unsigned /*version*/) {
35
		// nothing
36
	}
37
38
private:
39
	MSXAudio& audio;
40
};
41
REGISTER_POLYMORPHIC_INITIALIZER(Y8950Periphery, MusicModulePeriphery, "MusicModule");
42
43
class PanasonicAudioPeriphery final : public Y8950Periphery
44
{
45
public:
46
	PanasonicAudioPeriphery(
47
		MSXAudio& audio, DeviceConfig& config,
48
		const std::string& soundDeviceName);
49
	PanasonicAudioPeriphery(const PanasonicAudioPeriphery&) = delete;
50
	PanasonicAudioPeriphery(PanasonicAudioPeriphery&&) = delete;
51
	PanasonicAudioPeriphery& operator=(const PanasonicAudioPeriphery&) = delete;
52
	PanasonicAudioPeriphery& operator=(PanasonicAudioPeriphery&&) = delete;
53
	~PanasonicAudioPeriphery() override;
54
55
	void reset() override;
56
57
	void write(uint4_t outputs, uint4_t values, EmuTime time) override;
58
	[[nodiscard]] uint4_t read(EmuTime time) override;
59
60
	[[nodiscard]] uint8_t peekMem(uint16_t address, EmuTime time) const override;
61
	void writeMem(uint16_t address, uint8_t value, EmuTime time) override;
62
	[[nodiscard]] const uint8_t* getReadCacheLine(uint16_t address) const override;
63
	[[nodiscard]] uint8_t* getWriteCacheLine(uint16_t address) override;
64
65
	template<typename Archive>
66
	void serialize(Archive& ar, unsigned version);
67
68
private:
69
	void setBank(uint8_t value);
70
	void setIOPorts(uint8_t value);
71
	void setIOPortsHelper(uint8_t base, bool enable);
72
73
private:
74
	MSXAudio& audio;
75
	BooleanSetting swSwitch;
76
	Ram ram;
77
	Rom rom;
78
	uint8_t bankSelect;
79
	uint8_t ioPorts = 0;
80
};
81
REGISTER_POLYMORPHIC_INITIALIZER(Y8950Periphery, PanasonicAudioPeriphery, "Panasonic");
82
83
class ToshibaAudioPeriphery final : public Y8950Periphery
84
{
85
public:
86
	explicit ToshibaAudioPeriphery(MSXAudio& audio);
87
	void write(uint4_t outputs, uint4_t values, EmuTime time) override;
88
	[[nodiscard]] uint4_t read(EmuTime time) override;
89
	void setSPOFF(bool value, EmuTime time) override;
90
91
	template<typename Archive>
92
	void serialize(Archive& /*ar*/, unsigned /*version*/) {
93
		// nothing
94
	}
95
96
private:
97
	MSXAudio& audio;
98
};
99
REGISTER_POLYMORPHIC_INITIALIZER(Y8950Periphery, ToshibaAudioPeriphery, "Toshiba");
100
101
102
// Base class implementation:
103
104
void Y8950Periphery::reset()
105
{
106
	// nothing
107
}
108
109
void Y8950Periphery::setSPOFF(bool /*value*/, EmuTime /*time*/)
110
{
111
	// nothing
112
}
113
114
uint8_t Y8950Periphery::readMem(uint16_t address, EmuTime time)
115
{
116
	// by default do same as peekMem()
117
	return peekMem(address, time);
118
}
119
uint8_t Y8950Periphery::peekMem(uint16_t /*address*/, EmuTime /*time*/) const
120
{
121
	return 0xFF;
122
}
123
void Y8950Periphery::writeMem(uint16_t /*address*/, uint8_t /*value*/, EmuTime /*time*/)
124
{
125
	// nothing
126
}
127
const uint8_t* Y8950Periphery::getReadCacheLine(uint16_t /*address*/) const
128
{
129
	return MSXDevice::unmappedRead.data();
130
}
131
uint8_t* Y8950Periphery::getWriteCacheLine(uint16_t /*address*/)
132
{
133
	return MSXDevice::unmappedWrite.data();
134
}
135
136
137
// MusicModulePeriphery implementation:
138
139
MusicModulePeriphery::MusicModulePeriphery(MSXAudio& audio_)
140
	: audio(audio_)
141
{
142
}
143
144
void MusicModulePeriphery::write(uint4_t outputs, uint4_t values,
145
                                 EmuTime time)
146
{
147
	uint4_t actual = (outputs & values) | (~outputs & read(time));
148
	audio.y8950.setEnabled((actual & 8) != 0, time);
149
	audio.enableDAC((actual & 1) != 0, time);
150
}
151
152
uint4_t MusicModulePeriphery::read(EmuTime /*time*/)
153
{
154
	// IO2-IO1 are unconnected, reading them initially returns the last
155
	// written value, but after some seconds it falls back to '0'
156
	// IO3 and IO0 are output pins, but reading them return respectively
157
	// '1' and '0'
158
	return 8;
159
}
160
161
162
// PanasonicAudioPeriphery implementation:
163
164
PanasonicAudioPeriphery::PanasonicAudioPeriphery(
165
		MSXAudio& audio_, DeviceConfig& config,
166
		const std::string& soundDeviceName)
167
	: audio(audio_)
168
	, swSwitch(audio.getCommandController(), tmpStrCat(soundDeviceName, "_firmware"),
169
	           "This setting controls the switch on the Panasonic "
170
	           "MSX-AUDIO module. The switch controls whether the internal "
171
	           "software of this module must be started or not.",
172
	           false)
173
	// note: name + " RAM"  already taken by sample RAM
174
	, ram(config, audio.getName() + " mapped RAM",
175
	      "MSX-AUDIO mapped RAM", 0x1000)
176
	, rom(audio.getName() + " ROM", "MSX-AUDIO ROM", config)
177
{
178
	reset();
179
}
180
181
PanasonicAudioPeriphery::~PanasonicAudioPeriphery()
182
{
183
	setIOPorts(0); // unregister IO ports
184
}
185
186
void PanasonicAudioPeriphery::reset()
187
{
188
	ram.clear(); // TODO check
189
	setBank(0);
190
	setIOPorts(0); // TODO check: neither IO port ranges active
191
}
192
193
void PanasonicAudioPeriphery::write(uint4_t outputs, uint4_t values,
194
                                    EmuTime time)
195
{
196
	uint4_t actual = (outputs & values) | (~outputs & read(time));
197
	audio.y8950.setEnabled(!(actual & 8), time);
198
}
199
200
uint4_t PanasonicAudioPeriphery::read(EmuTime /*time*/)
201
{
202
	// verified bit 0,1,3 read as zero
203
	return swSwitch.getBoolean() ? 0x4 : 0x0; // bit2
204
}
205
206
uint8_t PanasonicAudioPeriphery::peekMem(uint16_t address, EmuTime /*time*/) const
207
{
208
	if ((bankSelect == 0) && ((address & 0x3FFF) >= 0x3000)) {
209
		return ram[(address & 0x3FFF) - 0x3000];
210
	} else {
211
		return rom[0x8000 * bankSelect + (address & 0x7FFF)];
212
	}
213
}
214
215
const uint8_t* PanasonicAudioPeriphery::getReadCacheLine(uint16_t address) const
216
{
217
	if ((bankSelect == 0) && ((address & 0x3FFF) >= 0x3000)) {
218
		return &ram[(address & 0x3FFF) - 0x3000];
219
	} else {
220
		return &rom[0x8000 * bankSelect + (address & 0x7FFF)];
221
	}
222
}
223
224
void PanasonicAudioPeriphery::writeMem(uint16_t address, uint8_t value, EmuTime /*time*/)
225
{
226
	address &= 0x7FFF;
227
	if (address == 0x7FFE) {
228
		setBank(value);
229
	} else if (address == 0x7FFF) {
230
		setIOPorts(value);
231
	}
232
	address &= 0x3FFF;
233
	if ((bankSelect == 0) && (address >= 0x3000)) {
234
		ram[address - 0x3000] = value;
235
	}
236
}
237
238
uint8_t* PanasonicAudioPeriphery::getWriteCacheLine(uint16_t address)
239
{
240
	address &= 0x7FFF;
241
	if (address == (0x7FFE & CacheLine::HIGH)) {
242
		return nullptr;
243
	}
244
	address &= 0x3FFF;
245
	if ((bankSelect == 0) && (address >= 0x3000)) {
246
		return &ram[address - 0x3000];
247
	} else {
248
		return MSXDevice::unmappedWrite.data();
249
	}
250
}
251
252
void PanasonicAudioPeriphery::setBank(uint8_t value)
253
{
254
	bankSelect = value & 3;
255
	audio.getCPU().invalidateAllSlotsRWCache(0x0000, 0x10000);
256
}
257
258
void PanasonicAudioPeriphery::setIOPorts(uint8_t value)
259
{
260
	uint8_t diff = ioPorts ^ value;
261
	if (diff & 1) {
262
		setIOPortsHelper(0xC0, (value & 1) != 0);
263
	}
264
	if (diff & 2) {
265
		setIOPortsHelper(0xC2, (value & 2) != 0);
266
	}
267
	ioPorts = value;
268
}
269
void PanasonicAudioPeriphery::setIOPortsHelper(uint8_t base, bool enable)
270
{
271
	MSXCPUInterface& cpu = audio.getCPUInterface();
272
	if (enable) {
273
		cpu.register_IO_InOut_range(base, 2, &audio);
274
	} else {
275
		cpu.unregister_IO_InOut_range(base, 2, &audio);
276
	}
277
}
278
279
template<typename Archive>
280
void PanasonicAudioPeriphery::serialize(Archive& ar, unsigned /*version*/)
281
{
282
	ar.serialize("ram",        ram,
283
	             "bankSelect", bankSelect);
284
	uint8_t tmpIoPorts = ioPorts;
285
	ar.serialize("ioPorts", tmpIoPorts);
286
	if constexpr (Archive::IS_LOADER) {
287
		setIOPorts(tmpIoPorts);
288
	}
289
}
290
291
292
// ToshibaAudioPeriphery implementation:
293
294
ToshibaAudioPeriphery::ToshibaAudioPeriphery(MSXAudio& audio_)
295
	: audio(audio_)
296
{
297
}
298
299
void ToshibaAudioPeriphery::write(uint4_t /*outputs*/, uint4_t /*values*/,
300
                                  EmuTime /*time*/)
301
{
302
	// TODO IO1-IO0 are programmed as output by HX-MU900 software rom
303
	//      and it writes periodically the values 1/1/2/2/0/0 to
304
	//      these pins, but I have no idea what function they have
305
}
306
307
uint4_t ToshibaAudioPeriphery::read(EmuTime /*time*/)
308
{
309
	// IO3-IO2 are unconnected (see also comment in MusicModulePeriphery)
310
	// IO1-IO0 are output pins, but reading them returns '1'
311
	return 0x3;
312
}
313
314
void ToshibaAudioPeriphery::setSPOFF(bool value, EmuTime time)
315
{
316
	audio.y8950.setEnabled(!value, time);
317
}
318
319
320
// Y8950PeripheryFactory implementation:
321
322
std::unique_ptr<Y8950Periphery> Y8950PeripheryFactory::create(
323
	MSXAudio& audio, DeviceConfig& config,
324
	const std::string& soundDeviceName)
325
{
326
	auto type = config.getChildData("type", "philips");
327
	StringOp::casecmp cmp;
328
	if (cmp(type, "philips")) {
329
		return std::make_unique<MusicModulePeriphery>(audio);
330
	} else if (cmp(type, "panasonic")) {
331
		return std::make_unique<PanasonicAudioPeriphery>(
332
			audio, config, soundDeviceName);
333
	} else if (cmp(type, "toshiba")) {
334
		return std::make_unique<ToshibaAudioPeriphery>(audio);
335
	}
336
	throw MSXException("Unknown MSX-AUDIO type: ", type);
337
}
338
339
} // namespace openmsx