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.hh"
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  virtual void write(nibble outputs, nibble values, EmuTime::param time);
29  virtual nibble read(EmuTime::param time);
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);
48 
49  virtual void reset();
50 
51  virtual void write(nibble outputs, nibble values, EmuTime::param time);
52  virtual nibble read(EmuTime::param time);
53 
54  virtual byte peekMem(word address, EmuTime::param time) const;
55  virtual void writeMem(word address, byte value, EmuTime::param time);
56  virtual const byte* getReadCacheLine(word start) const;
57  virtual byte* getWriteCacheLine(word start) const;
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  const std::unique_ptr<Ram> ram;
70  const std::unique_ptr<Rom> rom;
71  byte bankSelect;
72  byte ioPorts;
73 };
75 
77 {
78 public:
79  explicit ToshibaAudioPeriphery(MSXAudio& audio);
80  virtual void write(nibble outputs, nibble values, EmuTime::param time);
81  virtual nibble read(EmuTime::param time);
82  virtual void setSPOFF(bool value, EmuTime::param time);
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 }
100 
102 {
103  // nothing
104 }
105 
106 void Y8950Periphery::setSPOFF(bool /*value*/, EmuTime::param /*time*/)
107 {
108  // nothing
109 }
110 
112 {
113  // by default do same as peekMem()
114  return peekMem(address, time);
115 }
116 byte Y8950Periphery::peekMem(word /*address*/, EmuTime::param /*time*/) const
117 {
118  return 0xFF;
119 }
120 void Y8950Periphery::writeMem(word /*address*/, byte /*value*/, EmuTime::param /*time*/)
121 {
122  // nothing
123 }
125 {
127 }
129 {
131 }
132 
133 
134 // MusicModulePeriphery implementation:
135 
137  : audio(audio_)
138 {
139 }
140 
142  EmuTime::param time)
143 {
144  nibble actual = (outputs & values) | (~outputs & read(time));
145  audio.y8950->setEnabled((actual & 8) != 0, time);
146  audio.enableDAC((actual & 1) != 0, time);
147 }
148 
150 {
151  // IO2-IO1 are unconnected, reading them initially returns the last
152  // written value, but after some seconds it falls back to '0'
153  // IO3 and IO0 are output pins, but reading them return respectively
154  // '1' and '0'
155  return 8;
156 }
157 
158 
159 // PanasonicAudioPeriphery implementation:
160 
162  MSXAudio& audio_, const DeviceConfig& config,
163  const string& soundDeviceName)
164  : audio(audio_)
165  , swSwitch(audio.getCommandController(), soundDeviceName + "_firmware",
166  "This setting controls the switch on the Panasonic "
167  "MSX-AUDIO module. The switch controls whether the internal "
168  "software of this module must be started or not.",
169  false)
170  // note: name + " RAM" already taken by sample RAM
171  , ram(make_unique<Ram>(
172  config, audio.getName() + " mapped RAM",
173  "MSX-AUDIO mapped RAM", 0x1000))
174  , rom(make_unique<Rom>(
175  audio.getName() + " ROM", "MSX-AUDIO ROM", config))
176  , ioPorts(0)
177 {
178  reset();
179 }
180 
182 {
183  setIOPorts(0); // unregister IO ports
184 }
185 
187 {
188  ram->clear(); // TODO check
189  setBank(0);
190  setIOPorts(0); // TODO check: neither IO port ranges active
191 }
192 
194  EmuTime::param time)
195 {
196  nibble actual = (outputs & values) | (~outputs & read(time));
197  audio.y8950->setEnabled(!(actual & 8), time);
198 }
199 
201 {
202  // verified bit 0,1,3 read as zero
203  return swSwitch.getBoolean() ? 0x4 : 0x0; // bit2
204 }
205 
207 {
208  if ((bankSelect == 0) && ((address & 0x3FFF) >= 0x3000)) {
209  return (*ram)[(address & 0x3FFF) - 0x3000];
210  } else {
211  return (*rom)[0x8000 * bankSelect + (address & 0x7FFF)];
212  }
213 }
214 
216 {
217  if ((bankSelect == 0) && ((address & 0x3FFF) >= 0x3000)) {
218  return &(*ram)[(address & 0x3FFF) - 0x3000];
219  } else {
220  return &(*rom)[0x8000 * bankSelect + (address & 0x7FFF)];
221  }
222 }
223 
225 {
226  address &= 0x7FFF;
227  if (address == 0x7FFE) {
228  setBank(value);
229  } else if (address == 0x7FFF) {
230  setIOPorts(value);
231  }
232  address &= 0x3FFF;
233  if ((bankSelect == 0) && (address >= 0x3000)) {
234  (*ram)[address - 0x3000] = value;
235  }
236 }
237 
239 {
240  address &= 0x7FFF;
241  if (address == (0x7FFE & CacheLine::HIGH)) {
242  return nullptr;
243  }
244  address &= 0x3FFF;
245  if ((bankSelect == 0) && (address >= 0x3000)) {
246  return const_cast<byte*>(&(*ram)[address - 0x3000]);
247  } else {
249  }
250 }
251 
252 void PanasonicAudioPeriphery::setBank(byte value)
253 {
254  bankSelect = value & 3;
255  audio.getCPU().invalidateMemCache(0x0000, 0x10000);
256 }
257 
258 void PanasonicAudioPeriphery::setIOPorts(byte value)
259 {
260  byte diff = ioPorts ^ value;
261  if (diff & 1) {
262  setIOPortsHelper(0xC0, (value & 1) != 0);
263  }
264  if (diff & 2) {
265  setIOPortsHelper(0xC2, (value & 2) != 0);
266  }
267  ioPorts = value;
268 }
269 void PanasonicAudioPeriphery::setIOPortsHelper(unsigned base, bool enable)
270 {
271  MSXCPUInterface& cpu = audio.getCPUInterface();
272  if (enable) {
273  cpu.register_IO_In (base + 0, &audio);
274  cpu.register_IO_In (base + 1, &audio);
275  cpu.register_IO_Out(base + 0, &audio);
276  cpu.register_IO_Out(base + 1, &audio);
277  } else {
278  cpu.unregister_IO_In (base + 0, &audio);
279  cpu.unregister_IO_In (base + 1, &audio);
280  cpu.unregister_IO_Out(base + 0, &audio);
281  cpu.unregister_IO_Out(base + 1, &audio);
282  }
283 }
284 
285 template<typename Archive>
286 void PanasonicAudioPeriphery::serialize(Archive& ar, unsigned /*version*/)
287 {
288  ar.serialize("ram", *ram);
289  ar.serialize("bankSelect", bankSelect);
290  byte tmpIoPorts = ioPorts;
291  ar.serialize("ioPorts", tmpIoPorts);
292  if (ar.isLoader()) {
293  setIOPorts(tmpIoPorts);
294  }
295 }
296 
297 
298 // ToshibaAudioPeriphery implementation:
299 
301  : audio(audio_)
302 {
303 }
304 
305 void ToshibaAudioPeriphery::write(nibble /*outputs*/, nibble /*values*/,
306  EmuTime::param /*time*/)
307 {
308  // TODO IO1-IO0 are programmed as output by HX-MU900 software rom
309  // and it writes periodically the values 1/1/2/2/0/0 to
310  // these pins, but I have no idea what function they have
311 }
312 
314 {
315  // IO3-IO2 are unconnected (see also comment in MusicModulePeriphery)
316  // IO1-IO0 are output pins, but reading them returns '1'
317  return 0x3;
318 }
319 
321 {
322  audio.y8950->setEnabled(!value, time);
323 }
324 
325 
326 // Y8950PeripheryFactory implementation:
327 
328 std::unique_ptr<Y8950Periphery> Y8950PeripheryFactory::create(
329  MSXAudio& audio, const DeviceConfig& config,
330  const std::string& soundDeviceName)
331 {
332  string type(StringOp::toLower(config.getChildData("type", "philips")));
333  if (type == "philips") {
334  return make_unique<MusicModulePeriphery>(audio);
335  } else if (type == "panasonic") {
336  return make_unique<PanasonicAudioPeriphery>(
337  audio, config, soundDeviceName);
338  } else if (type == "toshiba") {
339  return make_unique<ToshibaAudioPeriphery>(audio);
340  } else {
341  throw MSXException("Unknown MSX-AUDIO type: " + type);
342  }
343 }
344 
345 } // namespace openmsx
PanasonicAudioPeriphery(MSXAudio &audio, const DeviceConfig &config, const string &soundDeviceName)
detail::KeyRange< MAP, 1 > values(const MAP &map)
Definition: KeyRange.hh:101
virtual nibble read(EmuTime::param time)
Read from (some of) the pins Some of the pins might be programmed as output, but this method doesn't ...
MSXCPUInterface & getCPUInterface() const
Definition: MSXDevice.cc:144
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, CassettePlayer,"CassettePlayer")
virtual nibble read(EmuTime::param time)
Read from (some of) the pins Some of the pins might be programmed as output, but this method doesn't ...
string toLower(string_ref str)
Definition: StringOp.cc:234
virtual byte readMem(word address, EmuTime::param time)
ToshibaAudioPeriphery(MSXAudio &audio)
unsigned char byte
8 bit unsigned integer
Definition: openmsx.hh:33
void serialize(Archive &, unsigned)
const std::string & getChildData(string_ref name) const
Definition: DeviceConfig.cc:43
virtual void write(nibble outputs, nibble values, EmuTime::param time)
Write to (some of) the pins.
virtual byte peekMem(word address, EmuTime::param time) const
virtual void setSPOFF(bool value, EmuTime::param time)
SP-OFF bit (bit 3 in Y8950 register 7)
virtual void writeMem(word address, byte value, EmuTime::param time)
static std::unique_ptr< Y8950Periphery > create(MSXAudio &audio, const DeviceConfig &config, const std::string &soundDeviceName)
void invalidateMemCache(word start, unsigned size)
Invalidate the CPU its cache for the interval [start, start + size) For example MSXMemoryMapper and M...
Definition: MSXCPU.cc:196
unsigned char nibble
4 bit integer
Definition: openmsx.hh:28
void register_IO_In(byte port, MSXDevice *device)
Devices can register their In ports.
virtual void setSPOFF(bool value, EmuTime::param time)
SP-OFF bit (bit 3 in Y8950 register 7)
virtual void write(nibble outputs, nibble values, EmuTime::param time)
Write to (some of) the pins.
virtual void writeMem(word address, byte value, EmuTime::param time)
virtual const byte * getReadCacheLine(word start) const
unsigned short word
16 bit unsigned integer
Definition: openmsx.hh:38
MSXCPU & getCPU() const
Definition: MSXDevice.cc:140
virtual byte * getWriteCacheLine(word start) const
virtual void write(nibble outputs, nibble values, EmuTime::param time)
Write to (some of) the pins.
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:266
void serialize(Archive &, unsigned)
MusicModulePeriphery(MSXAudio &audio)
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:267
void serialize(Archive &ar, unsigned version)
virtual byte peekMem(word address, EmuTime::param time) const
virtual nibble read(EmuTime::param time)
Read from (some of) the pins Some of the pins might be programmed as output, but this method doesn't ...
const string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:363
virtual const byte * getReadCacheLine(word start) const
Models the 4 general purpose I/O pins on the Y8950 (controlled by registers r#18 and r#19) ...
std::unique_ptr< T > make_unique()
Definition: memory.hh:27
virtual byte * getWriteCacheLine(word start) const