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 "CommandController.hh"
12 #include "MSXException.hh"
13 #include "StringOp.hh"
14 #include "DeviceConfig.hh"
15 #include "serialize.hh"
16 #include "memory.hh"
17 #include <string>
18 
19 using std::string;
20 
21 namespace openmsx {
22 
23 // Subclass declarations:
24 
26 {
27 public:
28  explicit MusicModulePeriphery(MSXAudio& audio);
29  virtual void write(nibble outputs, nibble values, EmuTime::param time);
30  virtual nibble read(EmuTime::param time);
31 
32  template<typename Archive>
33  void serialize(Archive& /*ar*/, unsigned /*version*/) {
34  // nothing
35  }
36 
37 private:
38  MSXAudio& audio;
39 };
40 REGISTER_POLYMORPHIC_INITIALIZER(Y8950Periphery, MusicModulePeriphery, "MusicModule");
41 
43 {
44 public:
46  MSXAudio& audio, const DeviceConfig& config,
47  const string& soundDeviceName);
49 
50  virtual void reset();
51 
52  virtual void write(nibble outputs, nibble values, EmuTime::param time);
53  virtual nibble read(EmuTime::param time);
54 
55  virtual byte peekMem(word address, EmuTime::param time) const;
56  virtual void writeMem(word address, byte value, EmuTime::param time);
57  virtual const byte* getReadCacheLine(word start) const;
58  virtual byte* getWriteCacheLine(word start) const;
59 
60  template<typename Archive>
61  void serialize(Archive& ar, unsigned version);
62 
63 private:
64  void setBank(byte value);
65  void setIOPorts(byte value);
66  void setIOPortsHelper(unsigned base, bool enable);
67 
68  MSXAudio& audio;
69  BooleanSetting swSwitch;
70  const std::unique_ptr<Ram> ram;
71  const std::unique_ptr<Rom> rom;
72  byte bankSelect;
73  byte ioPorts;
74 };
76 
78 {
79 public:
80  explicit ToshibaAudioPeriphery(MSXAudio& audio);
81  virtual void write(nibble outputs, nibble values, EmuTime::param time);
82  virtual nibble read(EmuTime::param time);
83  virtual void setSPOFF(bool value, EmuTime::param time);
84 
85  template<typename Archive>
86  void serialize(Archive& /*ar*/, unsigned /*version*/) {
87  // nothing
88  }
89 
90 private:
91  MSXAudio& audio;
92 };
93 REGISTER_POLYMORPHIC_INITIALIZER(Y8950Periphery, ToshibaAudioPeriphery, "Toshiba");
94 
95 
96 // Base class implementation:
97 
99 {
100 }
101 
103 {
104  // nothing
105 }
106 
107 void Y8950Periphery::setSPOFF(bool /*value*/, EmuTime::param /*time*/)
108 {
109  // nothing
110 }
111 
113 {
114  // by default do same as peekMem()
115  return peekMem(address, time);
116 }
117 byte Y8950Periphery::peekMem(word /*address*/, EmuTime::param /*time*/) const
118 {
119  return 0xFF;
120 }
121 void Y8950Periphery::writeMem(word /*address*/, byte /*value*/, EmuTime::param /*time*/)
122 {
123  // nothing
124 }
126 {
128 }
130 {
132 }
133 
134 
135 // MusicModulePeriphery implementation:
136 
138  : audio(audio_)
139 {
140 }
141 
143  EmuTime::param time)
144 {
145  nibble actual = (outputs & values) | (~outputs & read(time));
146  audio.y8950->setEnabled((actual & 8) != 0, time);
147  audio.enableDAC((actual & 1) != 0, time);
148 }
149 
151 {
152  // IO2-IO1 are unconnected, reading them initially returns the last
153  // written value, but after some seconds it falls back to '0'
154  // IO3 and IO0 are output pins, but reading them return respectively
155  // '1' and '0'
156  return 8;
157 }
158 
159 
160 // PanasonicAudioPeriphery implementation:
161 
163  MSXAudio& audio_, const DeviceConfig& config,
164  const string& soundDeviceName)
165  : audio(audio_)
166  , swSwitch(audio.getCommandController(), soundDeviceName + "_firmware",
167  "This setting controls the switch on the Panasonic "
168  "MSX-AUDIO module. The switch controls whether the internal "
169  "software of this module must be started or not.",
170  false)
171  // note: name + " RAM" already taken by sample RAM
172  , ram(make_unique<Ram>(
173  config, audio.getName() + " mapped RAM",
174  "MSX-AUDIO mapped RAM", 0x1000))
175  , rom(make_unique<Rom>(
176  audio.getName() + " ROM", "MSX-AUDIO ROM", config))
177  , ioPorts(0)
178 {
179  reset();
180 }
181 
183 {
184  setIOPorts(0); // unregister IO ports
185 }
186 
188 {
189  ram->clear(); // TODO check
190  setBank(0);
191  setIOPorts(0); // TODO check: neither IO port ranges active
192 }
193 
195  EmuTime::param time)
196 {
197  nibble actual = (outputs & values) | (~outputs & read(time));
198  audio.y8950->setEnabled(!(actual & 8), time);
199 }
200 
202 {
203  // verified bit 0,1,3 read as zero
204  return swSwitch.getValue() ? 0x4 : 0x0; // bit2
205 }
206 
208 {
209  if ((bankSelect == 0) && ((address & 0x3FFF) >= 0x3000)) {
210  return (*ram)[(address & 0x3FFF) - 0x3000];
211  } else {
212  return (*rom)[0x8000 * bankSelect + (address & 0x7FFF)];
213  }
214 }
215 
217 {
218  if ((bankSelect == 0) && ((address & 0x3FFF) >= 0x3000)) {
219  return &(*ram)[(address & 0x3FFF) - 0x3000];
220  } else {
221  return &(*rom)[0x8000 * bankSelect + (address & 0x7FFF)];
222  }
223 }
224 
226 {
227  address &= 0x7FFF;
228  if (address == 0x7FFE) {
229  setBank(value);
230  } else if (address == 0x7FFF) {
231  setIOPorts(value);
232  }
233  address &= 0x3FFF;
234  if ((bankSelect == 0) && (address >= 0x3000)) {
235  (*ram)[address - 0x3000] = value;
236  }
237 }
238 
240 {
241  address &= 0x7FFF;
242  if (address == (0x7FFE & CacheLine::HIGH)) {
243  return nullptr;
244  }
245  address &= 0x3FFF;
246  if ((bankSelect == 0) && (address >= 0x3000)) {
247  return const_cast<byte*>(&(*ram)[address - 0x3000]);
248  } else {
250  }
251 }
252 
253 void PanasonicAudioPeriphery::setBank(byte value)
254 {
255  bankSelect = value & 3;
256  audio.getCPU().invalidateMemCache(0x0000, 0x10000);
257 }
258 
259 void PanasonicAudioPeriphery::setIOPorts(byte value)
260 {
261  byte diff = ioPorts ^ value;
262  if (diff & 1) {
263  setIOPortsHelper(0xC0, (value & 1) != 0);
264  }
265  if (diff & 2) {
266  setIOPortsHelper(0xC2, (value & 2) != 0);
267  }
268  ioPorts = value;
269 }
270 void PanasonicAudioPeriphery::setIOPortsHelper(unsigned base, bool enable)
271 {
272  MSXCPUInterface& cpu = audio.getCPUInterface();
273  if (enable) {
274  cpu.register_IO_In (base + 0, &audio);
275  cpu.register_IO_In (base + 1, &audio);
276  cpu.register_IO_Out(base + 0, &audio);
277  cpu.register_IO_Out(base + 1, &audio);
278  } else {
279  cpu.unregister_IO_In (base + 0, &audio);
280  cpu.unregister_IO_In (base + 1, &audio);
281  cpu.unregister_IO_Out(base + 0, &audio);
282  cpu.unregister_IO_Out(base + 1, &audio);
283  }
284 }
285 
286 template<typename Archive>
287 void PanasonicAudioPeriphery::serialize(Archive& ar, unsigned /*version*/)
288 {
289  ar.serialize("ram", *ram);
290  ar.serialize("bankSelect", bankSelect);
291  byte tmpIoPorts = ioPorts;
292  ar.serialize("ioPorts", tmpIoPorts);
293  if (ar.isLoader()) {
294  setIOPorts(tmpIoPorts);
295  }
296 }
297 
298 
299 // ToshibaAudioPeriphery implementation:
300 
302  : audio(audio_)
303 {
304 }
305 
306 void ToshibaAudioPeriphery::write(nibble /*outputs*/, nibble /*values*/,
307  EmuTime::param /*time*/)
308 {
309  // TODO IO1-IO0 are programmed as output by HX-MU900 software rom
310  // and it writes periodically the values 1/1/2/2/0/0 to
311  // these pins, but I have no idea what function they have
312 }
313 
315 {
316  // IO3-IO2 are unconnected (see also comment in MusicModulePeriphery)
317  // IO1-IO0 are output pins, but reading them returns '1'
318  return 0x3;
319 }
320 
322 {
323  audio.y8950->setEnabled(!value, time);
324 }
325 
326 
327 // Y8950PeripheryFactory implementation:
328 
329 std::unique_ptr<Y8950Periphery> Y8950PeripheryFactory::create(
330  MSXAudio& audio, const DeviceConfig& config,
331  const std::string& soundDeviceName)
332 {
333  string type(StringOp::toLower(config.getChildData("type", "philips")));
334  if (type == "philips") {
335  return make_unique<MusicModulePeriphery>(audio);
336  } else if (type == "panasonic") {
337  return make_unique<PanasonicAudioPeriphery>(
338  audio, config, soundDeviceName);
339  } else if (type == "toshiba") {
340  return make_unique<ToshibaAudioPeriphery>(audio);
341  } else {
342  throw MSXException("Unknown MSX-AUDIO type: " + type);
343  }
344 }
345 
346 } // namespace openmsx