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