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 
18 using std::string;
19 
20 namespace openmsx {
21 
22 // Subclass declarations:
23 
25 {
26 public:
27  explicit MusicModulePeriphery(MSXAudio& audio);
28  void write(nibble outputs, nibble values, EmuTime::param time) override;
29  nibble read(EmuTime::param time) override;
30 
31  template<typename Archive>
32  void serialize(Archive& /*ar*/, unsigned /*version*/) {
33  // nothing
34  }
35 
36 private:
37  MSXAudio& audio;
38 };
39 REGISTER_POLYMORPHIC_INITIALIZER(Y8950Periphery, MusicModulePeriphery, "MusicModule");
40 
42 {
43 public:
45  MSXAudio& audio, const DeviceConfig& config,
46  const string& soundDeviceName);
47  ~PanasonicAudioPeriphery() override;
48 
49  void reset() override;
50 
51  void write(nibble outputs, nibble values, EmuTime::param time) override;
52  nibble read(EmuTime::param time) override;
53 
54  byte peekMem(word address, EmuTime::param time) const override;
55  void writeMem(word address, byte value, EmuTime::param time) override;
56  const byte* getReadCacheLine(word address) const override;
57  byte* getWriteCacheLine(word address) const override;
58 
59  template<typename Archive>
60  void serialize(Archive& ar, unsigned version);
61 
62 private:
63  void setBank(byte value);
64  void setIOPorts(byte value);
65  void setIOPortsHelper(unsigned base, bool enable);
66 
67  MSXAudio& audio;
68  BooleanSetting swSwitch;
69  Ram ram;
70  Rom rom;
71  byte bankSelect;
72  byte ioPorts;
73 };
75 
77 {
78 public:
79  explicit ToshibaAudioPeriphery(MSXAudio& audio);
80  void write(nibble outputs, nibble values, EmuTime::param time) override;
81  nibble read(EmuTime::param time) override;
82  void setSPOFF(bool value, EmuTime::param time) override;
83 
84  template<typename Archive>
85  void serialize(Archive& /*ar*/, unsigned /*version*/) {
86  // nothing
87  }
88 
89 private:
90  MSXAudio& audio;
91 };
92 REGISTER_POLYMORPHIC_INITIALIZER(Y8950Periphery, ToshibaAudioPeriphery, "Toshiba");
93 
94 
95 // Base class implementation:
96 
98 {
99  // nothing
100 }
101 
102 void Y8950Periphery::setSPOFF(bool /*value*/, EmuTime::param /*time*/)
103 {
104  // nothing
105 }
106 
107 byte Y8950Periphery::readMem(word address, EmuTime::param time)
108 {
109  // by default do same as peekMem()
110  return peekMem(address, time);
111 }
112 byte Y8950Periphery::peekMem(word /*address*/, EmuTime::param /*time*/) const
113 {
114  return 0xFF;
115 }
116 void Y8950Periphery::writeMem(word /*address*/, byte /*value*/, EmuTime::param /*time*/)
117 {
118  // nothing
119 }
120 const byte* Y8950Periphery::getReadCacheLine(word /*address*/) const
121 {
123 }
124 byte* Y8950Periphery::getWriteCacheLine(word /*address*/) const
125 {
127 }
128 
129 
130 // MusicModulePeriphery implementation:
131 
133  : audio(audio_)
134 {
135 }
136 
138  EmuTime::param time)
139 {
140  nibble actual = (outputs & values) | (~outputs & read(time));
141  audio.y8950.setEnabled((actual & 8) != 0, time);
142  audio.enableDAC((actual & 1) != 0, time);
143 }
144 
145 nibble MusicModulePeriphery::read(EmuTime::param /*time*/)
146 {
147  // IO2-IO1 are unconnected, reading them initially returns the last
148  // written value, but after some seconds it falls back to '0'
149  // IO3 and IO0 are output pins, but reading them return respectively
150  // '1' and '0'
151  return 8;
152 }
153 
154 
155 // PanasonicAudioPeriphery implementation:
156 
158  MSXAudio& audio_, const DeviceConfig& config,
159  const string& soundDeviceName)
160  : audio(audio_)
161  , swSwitch(audio.getCommandController(), soundDeviceName + "_firmware",
162  "This setting controls the switch on the Panasonic "
163  "MSX-AUDIO module. The switch controls whether the internal "
164  "software of this module must be started or not.",
165  false)
166  // note: name + " RAM" already taken by sample RAM
167  , ram(config, audio.getName() + " mapped RAM",
168  "MSX-AUDIO mapped RAM", 0x1000)
169  , rom(audio.getName() + " ROM", "MSX-AUDIO ROM", config)
170  , ioPorts(0)
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 
194 nibble PanasonicAudioPeriphery::read(EmuTime::param /*time*/)
195 {
196  // verified bit 0,1,3 read as zero
197  return swSwitch.getBoolean() ? 0x4 : 0x0; // bit2
198 }
199 
200 byte 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 
218 void 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 {
243  }
244 }
245 
246 void PanasonicAudioPeriphery::setBank(byte value)
247 {
248  bankSelect = value & 3;
249  audio.getCPU().invalidateAllSlotsRWCache(0x0000, 0x10000);
250 }
251 
252 void 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 }
263 void 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 
279 template<typename Archive>
280 void 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 (ar.isLoader()) {
287  setIOPorts(tmpIoPorts);
288  }
289 }
290 
291 
292 // ToshibaAudioPeriphery implementation:
293 
295  : audio(audio_)
296 {
297 }
298 
299 void 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 
307 nibble 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 
314 void ToshibaAudioPeriphery::setSPOFF(bool value, EmuTime::param 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, const DeviceConfig& config,
324  const std::string& soundDeviceName)
325 {
326  string type(StringOp::toLower(config.getChildData("type", "philips")));
327  if (type == "philips") {
328  return std::make_unique<MusicModulePeriphery>(audio);
329  } else if (type == "panasonic") {
330  return std::make_unique<PanasonicAudioPeriphery>(
331  audio, config, soundDeviceName);
332  } else if (type == "toshiba") {
333  return std::make_unique<ToshibaAudioPeriphery>(audio);
334  } else {
335  throw MSXException("Unknown MSX-AUDIO type: ", type);
336  }
337 }
338 
339 } // namespace openmsx
openmsx::Y8950Periphery::setSPOFF
virtual void setSPOFF(bool value, EmuTime::param time)
SP-OFF bit (bit 3 in Y8950 register 7)
Definition: Y8950Periphery.cc:102
openmsx::Y8950::setEnabled
void setEnabled(bool enabled, EmuTime::param time)
Definition: Y8950.cc:807
openmsx::Y8950Periphery::getReadCacheLine
virtual const byte * getReadCacheLine(word start) const
Definition: Y8950Periphery.cc:120
openmsx::PanasonicAudioPeriphery::getWriteCacheLine
byte * getWriteCacheLine(word address) const override
Definition: Y8950Periphery.cc:232
serialize.hh
openmsx::PanasonicAudioPeriphery::write
void write(nibble outputs, nibble values, EmuTime::param time) override
Write to (some of) the pins.
Definition: Y8950Periphery.cc:187
Rom.hh
openmsx::PanasonicAudioPeriphery::writeMem
void writeMem(word address, byte value, EmuTime::param time) override
Definition: Y8950Periphery.cc:218
openmsx::DeviceConfig
Definition: DeviceConfig.hh:19
openmsx::MSXDevice::unmappedWrite
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:293
openmsx::ToshibaAudioPeriphery::read
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 ...
Definition: Y8950Periphery.cc:307
MSXCPU.hh
MSXAudio.hh
openmsx::PanasonicAudioPeriphery::read
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 ...
Definition: Y8950Periphery.cc:194
MSXException.hh
openmsx::MSXException
Definition: MSXException.hh:9
openmsx::MSXAudio
Definition: MSXAudio.hh:14
openmsx::ToshibaAudioPeriphery::serialize
void serialize(Archive &, unsigned)
Definition: Y8950Periphery.cc:85
openmsx::MSXDevice::getCPUInterface
MSXCPUInterface & getCPUInterface() const
Definition: MSXDevice.cc:138
openmsx::CacheLine::HIGH
constexpr unsigned HIGH
Definition: CacheLine.hh:10
Y8950.hh
BooleanSetting.hh
openmsx::Y8950Periphery
Models the 4 general purpose I/O pins on the Y8950 (controlled by registers r#18 and r#19)
Definition: Y8950Periphery.hh:14
openmsx::PanasonicAudioPeriphery::PanasonicAudioPeriphery
PanasonicAudioPeriphery(MSXAudio &audio, const DeviceConfig &config, const string &soundDeviceName)
Definition: Y8950Periphery.cc:157
openmsx::PanasonicAudioPeriphery::serialize
void serialize(Archive &ar, unsigned version)
Definition: Y8950Periphery.cc:280
Ram.hh
StringOp::toLower
string toLower(string_view str)
Definition: StringOp.cc:64
openmsx::PanasonicAudioPeriphery::~PanasonicAudioPeriphery
~PanasonicAudioPeriphery() override
Definition: Y8950Periphery.cc:175
openmsx::Y8950PeripheryFactory::create
static std::unique_ptr< Y8950Periphery > create(MSXAudio &audio, const DeviceConfig &config, const std::string &soundDeviceName)
Definition: Y8950Periphery.cc:322
openmsx::PanasonicAudioPeriphery
Definition: Y8950Periphery.cc:41
openmsx::PanasonicAudioPeriphery::peekMem
byte peekMem(word address, EmuTime::param time) const override
Definition: Y8950Periphery.cc:200
openmsx::ToshibaAudioPeriphery::setSPOFF
void setSPOFF(bool value, EmuTime::param time) override
SP-OFF bit (bit 3 in Y8950 register 7)
Definition: Y8950Periphery.cc:314
openmsx::MSXCPU::invalidateAllSlotsRWCache
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:182
openmsx::Keys::getName
string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:589
openmsx::nibble
uint8_t nibble
4 bit integer
Definition: openmsx.hh:23
openmsx::DeviceConfig::getChildData
const std::string & getChildData(std::string_view name) const
Definition: DeviceConfig.cc:43
openmsx::ToshibaAudioPeriphery::ToshibaAudioPeriphery
ToshibaAudioPeriphery(MSXAudio &audio)
Definition: Y8950Periphery.cc:294
MSXDevice.hh
openmsx::Y8950Periphery::getWriteCacheLine
virtual byte * getWriteCacheLine(word start) const
Definition: Y8950Periphery.cc:124
view::values
auto values(Map &&map)
Definition: view.hh:317
Y8950Periphery.hh
CacheLine.hh
openmsx::MSXCPUInterface::register_IO_In
void register_IO_In(byte port, MSXDevice *device)
Devices can register their In ports.
Definition: MSXCPUInterface.cc:338
openmsx::MusicModulePeriphery::serialize
void serialize(Archive &, unsigned)
Definition: Y8950Periphery.cc:32
openmsx::Rom
Definition: Rom.hh:20
openmsx::Ram::clear
void clear(byte c=0xff)
Definition: Ram.cc:51
openmsx::MSXCPUInterface
MSXCPUInterface
Definition: MSXCPUInterface.cc:1274
openmsx::PanasonicAudioPeriphery::reset
void reset() override
Definition: Y8950Periphery.cc:180
openmsx::Y8950Periphery::reset
virtual void reset()
Definition: Y8950Periphery.cc:97
openmsx::ToshibaAudioPeriphery
Definition: Y8950Periphery.cc:76
openmsx::MusicModulePeriphery::read
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 ...
Definition: Y8950Periphery.cc:145
openmsx::Ram
Definition: Ram.hh:15
StringOp.hh
openmsx::Y8950Periphery::readMem
virtual byte readMem(word address, EmuTime::param time)
Definition: Y8950Periphery.cc:107
openmsx::ToshibaAudioPeriphery::write
void write(nibble outputs, nibble values, EmuTime::param time) override
Write to (some of) the pins.
Definition: Y8950Periphery.cc:299
openmsx::MSXDevice::unmappedRead
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:292
openmsx::word
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
MSXCPUInterface.hh
openmsx::BooleanSetting
Definition: BooleanSetting.hh:8
openmsx::Y8950Periphery::writeMem
virtual void writeMem(word address, byte value, EmuTime::param time)
Definition: Y8950Periphery.cc:116
openmsx::PanasonicAudioPeriphery::getReadCacheLine
const byte * getReadCacheLine(word address) const override
Definition: Y8950Periphery.cc:209
openmsx::MusicModulePeriphery::write
void write(nibble outputs, nibble values, EmuTime::param time) override
Write to (some of) the pins.
Definition: Y8950Periphery.cc:137
openmsx::MusicModulePeriphery::MusicModulePeriphery
MusicModulePeriphery(MSXAudio &audio)
Definition: Y8950Periphery.cc:132
DeviceConfig.hh
openmsx
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
openmsx::REGISTER_POLYMORPHIC_INITIALIZER
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, CassettePlayer, "CassettePlayer")
openmsx::MusicModulePeriphery
Definition: Y8950Periphery.cc:24
openmsx::BooleanSetting::getBoolean
bool getBoolean() const noexcept
Definition: BooleanSetting.hh:17
openmsx::Y8950Periphery::peekMem
virtual byte peekMem(word address, EmuTime::param time) const
Definition: Y8950Periphery.cc:112
openmsx::MSXDevice::getCPU
MSXCPU & getCPU() const
Definition: MSXDevice.cc:134