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  [[nodiscard]] 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 };
40 
42 {
43 public:
45  MSXAudio& audio, const DeviceConfig& config,
46  const string& soundDeviceName);
47  ~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 
64 private:
65  void setBank(byte value);
66  void setIOPorts(byte value);
67  void setIOPortsHelper(unsigned base, bool enable);
68 
69 private:
70  MSXAudio& audio;
71  BooleanSetting swSwitch;
72  Ram ram;
73  Rom rom;
74  byte bankSelect;
75  byte ioPorts;
76 };
78 
80 {
81 public:
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 
92 private:
93  MSXAudio& audio;
94 };
96 
97 
98 // Base class implementation:
99 
101 {
102  // nothing
103 }
104 
105 void Y8950Periphery::setSPOFF(bool /*value*/, EmuTime::param /*time*/)
106 {
107  // nothing
108 }
109 
110 byte Y8950Periphery::readMem(word address, EmuTime::param time)
111 {
112  // by default do same as peekMem()
113  return peekMem(address, time);
114 }
115 byte Y8950Periphery::peekMem(word /*address*/, EmuTime::param /*time*/) const
116 {
117  return 0xFF;
118 }
119 void Y8950Periphery::writeMem(word /*address*/, byte /*value*/, EmuTime::param /*time*/)
120 {
121  // nothing
122 }
123 const byte* Y8950Periphery::getReadCacheLine(word /*address*/) const
124 {
126 }
127 byte* Y8950Periphery::getWriteCacheLine(word /*address*/) const
128 {
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 
148 nibble 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 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  , ioPorts(0)
174 {
175  reset();
176 }
177 
179 {
180  setIOPorts(0); // unregister IO ports
181 }
182 
184 {
185  ram.clear(); // TODO check
186  setBank(0);
187  setIOPorts(0); // TODO check: neither IO port ranges active
188 }
189 
191  EmuTime::param time)
192 {
193  nibble actual = (outputs & values) | (~outputs & read(time));
194  audio.y8950.setEnabled(!(actual & 8), time);
195 }
196 
197 nibble PanasonicAudioPeriphery::read(EmuTime::param /*time*/)
198 {
199  // verified bit 0,1,3 read as zero
200  return swSwitch.getBoolean() ? 0x4 : 0x0; // bit2
201 }
202 
203 byte PanasonicAudioPeriphery::peekMem(word address, EmuTime::param /*time*/) const
204 {
205  if ((bankSelect == 0) && ((address & 0x3FFF) >= 0x3000)) {
206  return ram[(address & 0x3FFF) - 0x3000];
207  } else {
208  return rom[0x8000 * bankSelect + (address & 0x7FFF)];
209  }
210 }
211 
213 {
214  if ((bankSelect == 0) && ((address & 0x3FFF) >= 0x3000)) {
215  return &ram[(address & 0x3FFF) - 0x3000];
216  } else {
217  return &rom[0x8000 * bankSelect + (address & 0x7FFF)];
218  }
219 }
220 
221 void PanasonicAudioPeriphery::writeMem(word address, byte value, EmuTime::param /*time*/)
222 {
223  address &= 0x7FFF;
224  if (address == 0x7FFE) {
225  setBank(value);
226  } else if (address == 0x7FFF) {
227  setIOPorts(value);
228  }
229  address &= 0x3FFF;
230  if ((bankSelect == 0) && (address >= 0x3000)) {
231  ram[address - 0x3000] = value;
232  }
233 }
234 
236 {
237  address &= 0x7FFF;
238  if (address == (0x7FFE & CacheLine::HIGH)) {
239  return nullptr;
240  }
241  address &= 0x3FFF;
242  if ((bankSelect == 0) && (address >= 0x3000)) {
243  return const_cast<byte*>(&ram[address - 0x3000]);
244  } else {
246  }
247 }
248 
249 void PanasonicAudioPeriphery::setBank(byte value)
250 {
251  bankSelect = value & 3;
252  audio.getCPU().invalidateAllSlotsRWCache(0x0000, 0x10000);
253 }
254 
255 void PanasonicAudioPeriphery::setIOPorts(byte value)
256 {
257  byte diff = ioPorts ^ value;
258  if (diff & 1) {
259  setIOPortsHelper(0xC0, (value & 1) != 0);
260  }
261  if (diff & 2) {
262  setIOPortsHelper(0xC2, (value & 2) != 0);
263  }
264  ioPorts = value;
265 }
266 void PanasonicAudioPeriphery::setIOPortsHelper(unsigned base, bool enable)
267 {
268  MSXCPUInterface& cpu = audio.getCPUInterface();
269  if (enable) {
270  cpu.register_IO_In (base + 0, &audio);
271  cpu.register_IO_In (base + 1, &audio);
272  cpu.register_IO_Out(base + 0, &audio);
273  cpu.register_IO_Out(base + 1, &audio);
274  } else {
275  cpu.unregister_IO_In (base + 0, &audio);
276  cpu.unregister_IO_In (base + 1, &audio);
277  cpu.unregister_IO_Out(base + 0, &audio);
278  cpu.unregister_IO_Out(base + 1, &audio);
279  }
280 }
281 
282 template<typename Archive>
283 void PanasonicAudioPeriphery::serialize(Archive& ar, unsigned /*version*/)
284 {
285  ar.serialize("ram", ram,
286  "bankSelect", bankSelect);
287  byte tmpIoPorts = ioPorts;
288  ar.serialize("ioPorts", tmpIoPorts);
289  if constexpr (Archive::IS_LOADER) {
290  setIOPorts(tmpIoPorts);
291  }
292 }
293 
294 
295 // ToshibaAudioPeriphery implementation:
296 
298  : audio(audio_)
299 {
300 }
301 
302 void ToshibaAudioPeriphery::write(nibble /*outputs*/, nibble /*values*/,
303  EmuTime::param /*time*/)
304 {
305  // TODO IO1-IO0 are programmed as output by HX-MU900 software rom
306  // and it writes periodically the values 1/1/2/2/0/0 to
307  // these pins, but I have no idea what function they have
308 }
309 
310 nibble ToshibaAudioPeriphery::read(EmuTime::param /*time*/)
311 {
312  // IO3-IO2 are unconnected (see also comment in MusicModulePeriphery)
313  // IO1-IO0 are output pins, but reading them returns '1'
314  return 0x3;
315 }
316 
317 void ToshibaAudioPeriphery::setSPOFF(bool value, EmuTime::param time)
318 {
319  audio.y8950.setEnabled(!value, time);
320 }
321 
322 
323 // Y8950PeripheryFactory implementation:
324 
325 std::unique_ptr<Y8950Periphery> Y8950PeripheryFactory::create(
326  MSXAudio& audio, const DeviceConfig& config,
327  const std::string& soundDeviceName)
328 {
329  auto type = config.getChildData("type", "philips");
330  StringOp::casecmp cmp;
331  if (cmp(type, "philips")) {
332  return std::make_unique<MusicModulePeriphery>(audio);
333  } else if (cmp(type, "panasonic")) {
334  return std::make_unique<PanasonicAudioPeriphery>(
335  audio, config, soundDeviceName);
336  } else if (cmp(type, "toshiba")) {
337  return std::make_unique<ToshibaAudioPeriphery>(audio);
338  }
339  throw MSXException("Unknown MSX-AUDIO type: ", type);
340 }
341 
342 } // namespace openmsx
bool getBoolean() const noexcept
const std::string & getChildData(std::string_view name) const
Definition: DeviceConfig.cc:43
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:183
MSXCPU & getCPU() const
Definition: MSXDevice.cc:135
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:301
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:302
MSXCPUInterface & getCPUInterface() const
Definition: MSXDevice.cc:139
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)
byte * getWriteCacheLine(word address) const override
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
PanasonicAudioPeriphery & operator=(const PanasonicAudioPeriphery &)=delete
PanasonicAudioPeriphery(MSXAudio &audio, const DeviceConfig &config, const string &soundDeviceName)
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:52
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:774
constexpr unsigned HIGH
Definition: CacheLine.hh:10
string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:742
This file implemented 3 utility functions:
Definition: Autofire.cc:5
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:317
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:659