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  Ram ram;
70  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(config, audio.getName() + " mapped RAM",
168  "MSX-AUDIO mapped RAM", 0x1000)
169  , rom(audio.getName() + " ROM", "MSX-AUDIO ROM", config)
170  , ioPorts(0)
171 {
172  reset();
173 }
174 
176 {
177  setIOPorts(0); // unregister IO ports
178 }
179 
181 {
182  ram.clear(); // TODO check
183  setBank(0);
184  setIOPorts(0); // TODO check: neither IO port ranges active
185 }
186 
188  EmuTime::param time)
189 {
190  nibble actual = (outputs & values) | (~outputs & read(time));
191  audio.y8950.setEnabled(!(actual & 8), time);
192 }
193 
195 {
196  // verified bit 0,1,3 read as zero
197  return swSwitch.getBoolean() ? 0x4 : 0x0; // bit2
198 }
199 
201 {
202  if ((bankSelect == 0) && ((address & 0x3FFF) >= 0x3000)) {
203  return ram[(address & 0x3FFF) - 0x3000];
204  } else {
205  return rom[0x8000 * bankSelect + (address & 0x7FFF)];
206  }
207 }
208 
210 {
211  if ((bankSelect == 0) && ((address & 0x3FFF) >= 0x3000)) {
212  return &ram[(address & 0x3FFF) - 0x3000];
213  } else {
214  return &rom[0x8000 * bankSelect + (address & 0x7FFF)];
215  }
216 }
217 
219 {
220  address &= 0x7FFF;
221  if (address == 0x7FFE) {
222  setBank(value);
223  } else if (address == 0x7FFF) {
224  setIOPorts(value);
225  }
226  address &= 0x3FFF;
227  if ((bankSelect == 0) && (address >= 0x3000)) {
228  ram[address - 0x3000] = value;
229  }
230 }
231 
233 {
234  address &= 0x7FFF;
235  if (address == (0x7FFE & CacheLine::HIGH)) {
236  return nullptr;
237  }
238  address &= 0x3FFF;
239  if ((bankSelect == 0) && (address >= 0x3000)) {
240  return const_cast<byte*>(&ram[address - 0x3000]);
241  } else {
243  }
244 }
245 
246 void PanasonicAudioPeriphery::setBank(byte value)
247 {
248  bankSelect = value & 3;
249  audio.getCPU().invalidateMemCache(0x0000, 0x10000);
250 }
251 
252 void PanasonicAudioPeriphery::setIOPorts(byte value)
253 {
254  byte diff = ioPorts ^ value;
255  if (diff & 1) {
256  setIOPortsHelper(0xC0, (value & 1) != 0);
257  }
258  if (diff & 2) {
259  setIOPortsHelper(0xC2, (value & 2) != 0);
260  }
261  ioPorts = value;
262 }
263 void PanasonicAudioPeriphery::setIOPortsHelper(unsigned base, bool enable)
264 {
265  MSXCPUInterface& cpu = audio.getCPUInterface();
266  if (enable) {
267  cpu.register_IO_In (base + 0, &audio);
268  cpu.register_IO_In (base + 1, &audio);
269  cpu.register_IO_Out(base + 0, &audio);
270  cpu.register_IO_Out(base + 1, &audio);
271  } else {
272  cpu.unregister_IO_In (base + 0, &audio);
273  cpu.unregister_IO_In (base + 1, &audio);
274  cpu.unregister_IO_Out(base + 0, &audio);
275  cpu.unregister_IO_Out(base + 1, &audio);
276  }
277 }
278 
279 template<typename Archive>
280 void PanasonicAudioPeriphery::serialize(Archive& ar, unsigned /*version*/)
281 {
282  ar.serialize("ram", ram);
283  ar.serialize("bankSelect", bankSelect);
284  byte tmpIoPorts = ioPorts;
285  ar.serialize("ioPorts", tmpIoPorts);
286  if (ar.isLoader()) {
287  setIOPorts(tmpIoPorts);
288  }
289 }
290 
291 
292 // ToshibaAudioPeriphery implementation:
293 
295  : audio(audio_)
296 {
297 }
298 
299 void ToshibaAudioPeriphery::write(nibble /*outputs*/, nibble /*values*/,
300  EmuTime::param /*time*/)
301 {
302  // TODO IO1-IO0 are programmed as output by HX-MU900 software rom
303  // and it writes periodically the values 1/1/2/2/0/0 to
304  // these pins, but I have no idea what function they have
305 }
306 
308 {
309  // IO3-IO2 are unconnected (see also comment in MusicModulePeriphery)
310  // IO1-IO0 are output pins, but reading them returns '1'
311  return 0x3;
312 }
313 
315 {
316  audio.y8950.setEnabled(!value, time);
317 }
318 
319 
320 // Y8950PeripheryFactory implementation:
321 
322 std::unique_ptr<Y8950Periphery> Y8950PeripheryFactory::create(
323  MSXAudio& audio, const DeviceConfig& config,
324  const std::string& soundDeviceName)
325 {
326  string type(StringOp::toLower(config.getChildData("type", "philips")));
327  if (type == "philips") {
328  return make_unique<MusicModulePeriphery>(audio);
329  } else if (type == "panasonic") {
330  return make_unique<PanasonicAudioPeriphery>(
331  audio, config, soundDeviceName);
332  } else if (type == "toshiba") {
333  return make_unique<ToshibaAudioPeriphery>(audio);
334  } else {
335  throw MSXException("Unknown MSX-AUDIO type: " + type);
336  }
337 }
338 
339 } // 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:54
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:235
void setEnabled(bool enabled, EmuTime::param time)
Definition: Y8950.cc:743
virtual byte readMem(word address, EmuTime::param time)
ToshibaAudioPeriphery(MSXAudio &audio)
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:147
void clear(byte c=0xff)
Definition: Ram.cc:53
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)
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
unsigned char byte
8 bit unsigned integer
Definition: openmsx.hh:25
unsigned char nibble
4 bit integer
Definition: openmsx.hh:22
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
unsigned short word
16 bit unsigned integer
Definition: openmsx.hh:28