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