openMSX
Y8950Periphery.cc
Go to the documentation of this file.
1#include "Y8950Periphery.hh"
2#include "Y8950.hh"
3#include "MSXAudio.hh"
4#include "MSXCPU.hh"
5#include "MSXCPUInterface.hh"
6#include "MSXDevice.hh"
7#include "CacheLine.hh"
8#include "Ram.hh"
9#include "Rom.hh"
10#include "BooleanSetting.hh"
11#include "MSXException.hh"
12#include "StringOp.hh"
13#include "DeviceConfig.hh"
14#include "serialize.hh"
15#include <memory>
16#include <string>
17
18namespace openmsx {
19
20// Subclass declarations:
21
23{
24public:
25 explicit MusicModulePeriphery(MSXAudio& audio);
26 void write(nibble outputs, nibble values, EmuTime::param time) override;
27 [[nodiscard]] nibble read(EmuTime::param time) override;
28
29 template<typename Archive>
30 void serialize(Archive& /*ar*/, unsigned /*version*/) {
31 // nothing
32 }
33
34private:
35 MSXAudio& audio;
36};
38
40{
41public:
43 MSXAudio& audio, const DeviceConfig& config,
44 const std::string& soundDeviceName);
45 ~PanasonicAudioPeriphery() override;
48
49 void reset() override;
50
51 void write(nibble outputs, nibble values, EmuTime::param time) override;
52 [[nodiscard]] nibble read(EmuTime::param time) override;
53
54 [[nodiscard]] byte peekMem(word address, EmuTime::param time) const override;
55 void writeMem(word address, byte value, EmuTime::param time) override;
56 [[nodiscard]] const byte* getReadCacheLine(word address) const override;
57 [[nodiscard]] byte* getWriteCacheLine(word address) const override;
58
59 template<typename Archive>
60 void serialize(Archive& ar, unsigned version);
61
62private:
63 void setBank(byte value);
64 void setIOPorts(byte value);
65 void setIOPortsHelper(unsigned base, bool enable);
66
67private:
68 MSXAudio& audio;
69 BooleanSetting swSwitch;
70 Ram ram;
71 Rom rom;
72 byte bankSelect;
73 byte ioPorts = 0;
74};
76
78{
79public:
80 explicit ToshibaAudioPeriphery(MSXAudio& audio);
81 void write(nibble outputs, nibble values, EmuTime::param time) override;
82 [[nodiscard]] nibble read(EmuTime::param time) override;
83 void setSPOFF(bool value, EmuTime::param time) override;
84
85 template<typename Archive>
86 void serialize(Archive& /*ar*/, unsigned /*version*/) {
87 // nothing
88 }
89
90private:
91 MSXAudio& audio;
92};
94
95
96// Base class implementation:
97
99{
100 // nothing
101}
102
103void Y8950Periphery::setSPOFF(bool /*value*/, EmuTime::param /*time*/)
104{
105 // nothing
106}
107
108byte Y8950Periphery::readMem(word address, EmuTime::param time)
109{
110 // by default do same as peekMem()
111 return peekMem(address, time);
112}
113byte Y8950Periphery::peekMem(word /*address*/, EmuTime::param /*time*/) const
114{
115 return 0xFF;
116}
117void Y8950Periphery::writeMem(word /*address*/, byte /*value*/, EmuTime::param /*time*/)
118{
119 // nothing
120}
121const byte* Y8950Periphery::getReadCacheLine(word /*address*/) const
122{
123 return MSXDevice::unmappedRead.data();
124}
126{
127 return MSXDevice::unmappedWrite.data();
128}
129
130
131// MusicModulePeriphery implementation:
132
134 : audio(audio_)
135{
136}
137
139 EmuTime::param time)
140{
141 nibble actual = (outputs & values) | (~outputs & read(time));
142 audio.y8950.setEnabled((actual & 8) != 0, time);
143 audio.enableDAC((actual & 1) != 0, time);
144}
145
146nibble MusicModulePeriphery::read(EmuTime::param /*time*/)
147{
148 // IO2-IO1 are unconnected, reading them initially returns the last
149 // written value, but after some seconds it falls back to '0'
150 // IO3 and IO0 are output pins, but reading them return respectively
151 // '1' and '0'
152 return 8;
153}
154
155
156// PanasonicAudioPeriphery implementation:
157
159 MSXAudio& audio_, const DeviceConfig& config,
160 const std::string& soundDeviceName)
161 : audio(audio_)
162 , swSwitch(audio.getCommandController(), tmpStrCat(soundDeviceName, "_firmware"),
163 "This setting controls the switch on the Panasonic "
164 "MSX-AUDIO module. The switch controls whether the internal "
165 "software of this module must be started or not.",
166 false)
167 // note: name + " RAM" already taken by sample RAM
168 , ram(config, audio.getName() + " mapped RAM",
169 "MSX-AUDIO mapped RAM", 0x1000)
170 , rom(audio.getName() + " ROM", "MSX-AUDIO ROM", config)
171{
172 reset();
173}
174
176{
177 setIOPorts(0); // unregister IO ports
178}
179
181{
182 ram.clear(); // TODO check
183 setBank(0);
184 setIOPorts(0); // TODO check: neither IO port ranges active
185}
186
188 EmuTime::param time)
189{
190 nibble actual = (outputs & values) | (~outputs & read(time));
191 audio.y8950.setEnabled(!(actual & 8), time);
192}
193
194nibble PanasonicAudioPeriphery::read(EmuTime::param /*time*/)
195{
196 // verified bit 0,1,3 read as zero
197 return swSwitch.getBoolean() ? 0x4 : 0x0; // bit2
198}
199
200byte PanasonicAudioPeriphery::peekMem(word address, EmuTime::param /*time*/) const
201{
202 if ((bankSelect == 0) && ((address & 0x3FFF) >= 0x3000)) {
203 return ram[(address & 0x3FFF) - 0x3000];
204 } else {
205 return rom[0x8000 * bankSelect + (address & 0x7FFF)];
206 }
207}
208
210{
211 if ((bankSelect == 0) && ((address & 0x3FFF) >= 0x3000)) {
212 return &ram[(address & 0x3FFF) - 0x3000];
213 } else {
214 return &rom[0x8000 * bankSelect + (address & 0x7FFF)];
215 }
216}
217
218void PanasonicAudioPeriphery::writeMem(word address, byte value, EmuTime::param /*time*/)
219{
220 address &= 0x7FFF;
221 if (address == 0x7FFE) {
222 setBank(value);
223 } else if (address == 0x7FFF) {
224 setIOPorts(value);
225 }
226 address &= 0x3FFF;
227 if ((bankSelect == 0) && (address >= 0x3000)) {
228 ram[address - 0x3000] = value;
229 }
230}
231
233{
234 address &= 0x7FFF;
235 if (address == (0x7FFE & CacheLine::HIGH)) {
236 return nullptr;
237 }
238 address &= 0x3FFF;
239 if ((bankSelect == 0) && (address >= 0x3000)) {
240 return const_cast<byte*>(&ram[address - 0x3000]);
241 } else {
242 return MSXDevice::unmappedWrite.data();
243 }
244}
245
246void PanasonicAudioPeriphery::setBank(byte value)
247{
248 bankSelect = value & 3;
249 audio.getCPU().invalidateAllSlotsRWCache(0x0000, 0x10000);
250}
251
252void PanasonicAudioPeriphery::setIOPorts(byte value)
253{
254 byte diff = ioPorts ^ value;
255 if (diff & 1) {
256 setIOPortsHelper(0xC0, (value & 1) != 0);
257 }
258 if (diff & 2) {
259 setIOPortsHelper(0xC2, (value & 2) != 0);
260 }
261 ioPorts = value;
262}
263void PanasonicAudioPeriphery::setIOPortsHelper(unsigned base, bool enable)
264{
265 MSXCPUInterface& cpu = audio.getCPUInterface();
266 if (enable) {
267 cpu.register_IO_In (base + 0, &audio);
268 cpu.register_IO_In (base + 1, &audio);
269 cpu.register_IO_Out(base + 0, &audio);
270 cpu.register_IO_Out(base + 1, &audio);
271 } else {
272 cpu.unregister_IO_In (base + 0, &audio);
273 cpu.unregister_IO_In (base + 1, &audio);
274 cpu.unregister_IO_Out(base + 0, &audio);
275 cpu.unregister_IO_Out(base + 1, &audio);
276 }
277}
278
279template<typename Archive>
280void PanasonicAudioPeriphery::serialize(Archive& ar, unsigned /*version*/)
281{
282 ar.serialize("ram", ram,
283 "bankSelect", bankSelect);
284 byte tmpIoPorts = ioPorts;
285 ar.serialize("ioPorts", tmpIoPorts);
286 if constexpr (Archive::IS_LOADER) {
287 setIOPorts(tmpIoPorts);
288 }
289}
290
291
292// ToshibaAudioPeriphery implementation:
293
295 : audio(audio_)
296{
297}
298
299void ToshibaAudioPeriphery::write(nibble /*outputs*/, nibble /*values*/,
300 EmuTime::param /*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
307nibble ToshibaAudioPeriphery::read(EmuTime::param /*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
314void ToshibaAudioPeriphery::setSPOFF(bool value, EmuTime::param time)
315{
316 audio.y8950.setEnabled(!value, time);
317}
318
319
320// Y8950PeripheryFactory implementation:
321
322std::unique_ptr<Y8950Periphery> Y8950PeripheryFactory::create(
323 MSXAudio& audio, const DeviceConfig& config,
324 const std::string& soundDeviceName)
325{
326 auto type = config.getChildData("type", "philips");
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
bool getBoolean() const noexcept
std::string_view getChildData(std::string_view name) const
Definition: DeviceConfig.cc:48
void register_IO_In(byte port, MSXDevice *device)
Devices can register their In ports.
void invalidateAllSlotsRWCache(word start, unsigned size)
Invalidate the CPU its cache for the interval [start, start + size) For example MSXMemoryMapper and M...
Definition: MSXCPU.cc:176
static std::array< byte, 0x10000 > unmappedRead
Definition: MSXDevice.hh:302
MSXCPU & getCPU() const
Definition: MSXDevice.cc:129
static std::array< byte, 0x10000 > unmappedWrite
Definition: MSXDevice.hh:303
MSXCPUInterface & getCPUInterface() const
Definition: MSXDevice.cc:133
void serialize(Archive &, unsigned)
void write(nibble outputs, nibble values, EmuTime::param time) override
Write to (some of) the pins.
nibble read(EmuTime::param time) override
Read from (some of) the pins Some of the pins might be programmed as output, but this method doesn't ...
MusicModulePeriphery(MSXAudio &audio)
PanasonicAudioPeriphery & operator=(const PanasonicAudioPeriphery &)=delete
byte * getWriteCacheLine(word address) const override
PanasonicAudioPeriphery(MSXAudio &audio, const DeviceConfig &config, const std::string &soundDeviceName)
nibble read(EmuTime::param time) override
Read from (some of) the pins Some of the pins might be programmed as output, but this method doesn't ...
byte peekMem(word address, EmuTime::param time) const override
void write(nibble outputs, nibble values, EmuTime::param time) override
Write to (some of) the pins.
void writeMem(word address, byte value, EmuTime::param time) override
const byte * getReadCacheLine(word address) const override
void serialize(Archive &ar, unsigned version)
PanasonicAudioPeriphery(const PanasonicAudioPeriphery &)=delete
void clear(byte c=0xff)
Definition: Ram.cc:35
nibble read(EmuTime::param time) override
Read from (some of) the pins Some of the pins might be programmed as output, but this method doesn't ...
void write(nibble outputs, nibble values, EmuTime::param time) override
Write to (some of) the pins.
void serialize(Archive &, unsigned)
void setSPOFF(bool value, EmuTime::param time) override
SP-OFF bit (bit 3 in Y8950 register 7)
ToshibaAudioPeriphery(MSXAudio &audio)
static std::unique_ptr< Y8950Periphery > create(MSXAudio &audio, const DeviceConfig &config, const std::string &soundDeviceName)
Models the 4 general purpose I/O pins on the Y8950 (controlled by registers r#18 and r#19)
virtual byte peekMem(word address, EmuTime::param time) const
virtual byte * getWriteCacheLine(word start) const
virtual const byte * getReadCacheLine(word start) const
virtual void writeMem(word address, byte value, EmuTime::param time)
virtual byte readMem(word address, EmuTime::param time)
virtual void setSPOFF(bool value, EmuTime::param time)
SP-OFF bit (bit 3 in Y8950 register 7)
void setEnabled(bool enabled, EmuTime::param time)
Definition: Y8950.cc:780
constexpr unsigned HIGH
Definition: CacheLine.hh:10
std::string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:730
This file implemented 3 utility functions:
Definition: Autofire.cc:9
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, CassettePlayer, "CassettePlayer")
uint8_t nibble
4 bit integer
Definition: openmsx.hh:23
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
constexpr auto values(Map &&map)
Definition: view.hh:469
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:610