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  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);
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 start) const override;
57  byte* getWriteCacheLine(word start) 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  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  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 
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 }
121 {
123 }
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 
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(make_unique<Ram>(
168  config, audio.getName() + " mapped RAM",
169  "MSX-AUDIO mapped RAM", 0x1000))
170  , rom(make_unique<Rom>(
171  audio.getName() + " ROM", "MSX-AUDIO ROM", config))
172  , ioPorts(0)
173 {
174  reset();
175 }
176 
178 {
179  setIOPorts(0); // unregister IO ports
180 }
181 
183 {
184  ram->clear(); // TODO check
185  setBank(0);
186  setIOPorts(0); // TODO check: neither IO port ranges active
187 }
188 
190  EmuTime::param time)
191 {
192  nibble actual = (outputs & values) | (~outputs & read(time));
193  audio.y8950->setEnabled(!(actual & 8), time);
194 }
195 
197 {
198  // verified bit 0,1,3 read as zero
199  return swSwitch.getBoolean() ? 0x4 : 0x0; // bit2
200 }
201 
203 {
204  if ((bankSelect == 0) && ((address & 0x3FFF) >= 0x3000)) {
205  return (*ram)[(address & 0x3FFF) - 0x3000];
206  } else {
207  return (*rom)[0x8000 * bankSelect + (address & 0x7FFF)];
208  }
209 }
210 
212 {
213  if ((bankSelect == 0) && ((address & 0x3FFF) >= 0x3000)) {
214  return &(*ram)[(address & 0x3FFF) - 0x3000];
215  } else {
216  return &(*rom)[0x8000 * bankSelect + (address & 0x7FFF)];
217  }
218 }
219 
221 {
222  address &= 0x7FFF;
223  if (address == 0x7FFE) {
224  setBank(value);
225  } else if (address == 0x7FFF) {
226  setIOPorts(value);
227  }
228  address &= 0x3FFF;
229  if ((bankSelect == 0) && (address >= 0x3000)) {
230  (*ram)[address - 0x3000] = value;
231  }
232 }
233 
235 {
236  address &= 0x7FFF;
237  if (address == (0x7FFE & CacheLine::HIGH)) {
238  return nullptr;
239  }
240  address &= 0x3FFF;
241  if ((bankSelect == 0) && (address >= 0x3000)) {
242  return const_cast<byte*>(&(*ram)[address - 0x3000]);
243  } else {
245  }
246 }
247 
248 void PanasonicAudioPeriphery::setBank(byte value)
249 {
250  bankSelect = value & 3;
251  audio.getCPU().invalidateMemCache(0x0000, 0x10000);
252 }
253 
254 void PanasonicAudioPeriphery::setIOPorts(byte value)
255 {
256  byte diff = ioPorts ^ value;
257  if (diff & 1) {
258  setIOPortsHelper(0xC0, (value & 1) != 0);
259  }
260  if (diff & 2) {
261  setIOPortsHelper(0xC2, (value & 2) != 0);
262  }
263  ioPorts = value;
264 }
265 void PanasonicAudioPeriphery::setIOPortsHelper(unsigned base, bool enable)
266 {
267  MSXCPUInterface& cpu = audio.getCPUInterface();
268  if (enable) {
269  cpu.register_IO_In (base + 0, &audio);
270  cpu.register_IO_In (base + 1, &audio);
271  cpu.register_IO_Out(base + 0, &audio);
272  cpu.register_IO_Out(base + 1, &audio);
273  } else {
274  cpu.unregister_IO_In (base + 0, &audio);
275  cpu.unregister_IO_In (base + 1, &audio);
276  cpu.unregister_IO_Out(base + 0, &audio);
277  cpu.unregister_IO_Out(base + 1, &audio);
278  }
279 }
280 
281 template<typename Archive>
282 void PanasonicAudioPeriphery::serialize(Archive& ar, unsigned /*version*/)
283 {
284  ar.serialize("ram", *ram);
285  ar.serialize("bankSelect", bankSelect);
286  byte tmpIoPorts = ioPorts;
287  ar.serialize("ioPorts", tmpIoPorts);
288  if (ar.isLoader()) {
289  setIOPorts(tmpIoPorts);
290  }
291 }
292 
293 
294 // ToshibaAudioPeriphery implementation:
295 
297  : audio(audio_)
298 {
299 }
300 
301 void ToshibaAudioPeriphery::write(nibble /*outputs*/, nibble /*values*/,
302  EmuTime::param /*time*/)
303 {
304  // TODO IO1-IO0 are programmed as output by HX-MU900 software rom
305  // and it writes periodically the values 1/1/2/2/0/0 to
306  // these pins, but I have no idea what function they have
307 }
308 
310 {
311  // IO3-IO2 are unconnected (see also comment in MusicModulePeriphery)
312  // IO1-IO0 are output pins, but reading them returns '1'
313  return 0x3;
314 }
315 
317 {
318  audio.y8950->setEnabled(!value, time);
319 }
320 
321 
322 // Y8950PeripheryFactory implementation:
323 
324 std::unique_ptr<Y8950Periphery> Y8950PeripheryFactory::create(
325  MSXAudio& audio, const DeviceConfig& config,
326  const std::string& soundDeviceName)
327 {
328  string type(StringOp::toLower(config.getChildData("type", "philips")));
329  if (type == "philips") {
330  return make_unique<MusicModulePeriphery>(audio);
331  } else if (type == "panasonic") {
332  return make_unique<PanasonicAudioPeriphery>(
333  audio, config, soundDeviceName);
334  } else if (type == "toshiba") {
335  return make_unique<ToshibaAudioPeriphery>(audio);
336  } else {
337  throw MSXException("Unknown MSX-AUDIO type: " + type);
338  }
339 }
340 
341 } // namespace openmsx
PanasonicAudioPeriphery(MSXAudio &audio, const DeviceConfig &config, const 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 ...
detail::KeyRange< MAP, 1 > values(const MAP &map)
Definition: KeyRange.hh:101
const byte * getReadCacheLine(word start) const override
MSXCPUInterface & getCPUInterface() const
Definition: MSXDevice.cc:144
void write(nibble outputs, nibble values, EmuTime::param time) override
Write to (some of) the pins.
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, CassettePlayer,"CassettePlayer")
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
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 serialize(Archive &, unsigned)
const std::string & getChildData(string_ref name) const
Definition: DeviceConfig.cc:43
virtual void writeMem(word address, byte value, EmuTime::param time)
void write(nibble outputs, nibble values, EmuTime::param time) override
Write to (some of) the pins.
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)
unsigned short word
16 bit unsigned integer
Definition: openmsx.hh:38
MSXCPU & getCPU() const
Definition: MSXDevice.cc:140
virtual byte * getWriteCacheLine(word start) const
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:266
void serialize(Archive &, unsigned)
void write(nibble outputs, nibble values, EmuTime::param time) override
Write to (some of) the pins.
MusicModulePeriphery(MSXAudio &audio)
void writeMem(word address, byte value, EmuTime::param time) override
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:267
void serialize(Archive &ar, unsigned version)
void setSPOFF(bool value, EmuTime::param time) override
SP-OFF bit (bit 3 in Y8950 register 7)
virtual byte peekMem(word address, EmuTime::param time) const
const string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:373
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) ...
byte * getWriteCacheLine(word start) 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
std::unique_ptr< T > make_unique()
Definition: memory.hh:27