openMSX
MSXMoonSound.cc
Go to the documentation of this file.
1 // ATM this class does several things:
2 // - It connects the YMF278b chip to specific I/O ports in the MSX machine
3 // - It glues the YMF262 (FM-part) and YMF278 (Wave-part) classes together in a
4 // full model of a YMF278b chip. IOW part of the logic of the YM278b is
5 // modeled here instead of in a chip-specific class.
6 // TODO it would be nice to move the functionality of the 2nd point to a
7 // different class, but until there's a 2nd user of this chip, this is
8 // low priority.
9 
10 #include "MSXMoonSound.hh"
11 #include "Clock.hh"
12 #include "serialize.hh"
13 #include "unreachable.hh"
14 
15 namespace openmsx {
16 
17 // The master clock, running at 33.8MHz.
19 
20 // Required delay between register select and register read/write.
21 static const EmuDuration FM_REG_SELECT_DELAY = MasterClock::duration(56);
22 // Required delay after register write.
23 static const EmuDuration FM_REG_WRITE_DELAY = MasterClock::duration(56);
24 // Datasheet doesn't mention any delay for reads from the FM registers. In fact
25 // it says reads from FM registers are not possible while tests on a real
26 // YMF278 show they do work (value of the NEW2 bit doesn't matter).
27 
28 // Required delay between register select and register read/write.
29 static const EmuDuration WAVE_REG_SELECT_DELAY = MasterClock::duration(88);
30 // Required delay after register write.
31 static const EmuDuration WAVE_REG_WRITE_DELAY = MasterClock::duration(88);
32 // Datasheet doesn't mention any delay for register reads (except for reads
33 // from register 6, see below). I also couldn't measure any delay on a real
34 // YMF278.
35 
36 // Required delay after memory read.
37 static const EmuDuration MEM_READ_DELAY = MasterClock::duration(38);
38 // Required delay after memory write (instead of register write delay).
39 static const EmuDuration MEM_WRITE_DELAY = MasterClock::duration(28);
40 
41 // Required delay after instrument load.
42 // We pick 10000 cycles, this is approximately 300us (the number given in the
43 // datasheet). The exact number of cycles is unknown. But I did some (very
44 // rough) tests on real HW, and this number is not too bad (slightly too high
45 // but within 2%-4% of real value, needs more detailed tests).
46 static const EmuDuration LOAD_DELAY = MasterClock::duration(10000);
47 
48 
50  : MSXDevice(config)
51  , ymf262(getName() + " FM", config, true)
52  , ymf278(getName() + " wave",
53  config.getChildDataAsInt("sampleram", 512), // size in kb
54  config)
55  , ymf278LoadTime(getCurrentTime())
56  , ymf278BusyTime(getCurrentTime())
57 {
59 }
60 
61 void MSXMoonSound::powerUp(EmuTime::param time)
62 {
63  ymf278.clearRam();
64  reset(time);
65 }
66 
67 void MSXMoonSound::reset(EmuTime::param time)
68 {
69  ymf262.reset(time);
70  ymf278.reset(time);
71 
72  opl4latch = 0; // TODO check
73  opl3latch = 0; // TODO check
74  alreadyReadID = false;
75 
76  ymf278BusyTime = time;
77  ymf278LoadTime = time;
78 }
79 
80 byte MSXMoonSound::readIO(word port, EmuTime::param time)
81 {
82  byte result;
83  if ((port & 0xFF) < 0xC0) {
84  // WAVE part 0x7E-0x7F
85  switch (port & 0x01) {
86  case 0: // read latch, not supported
87  result = 255;
88  break;
89  case 1: // read wave register
90  // Verified on real YMF278:
91  // Even if NEW2=0 reads happen normally. Also reads
92  // from sample memory (and thus the internal memory
93  // pointer gets increased).
94  if ((3 <= opl4latch) && (opl4latch <= 6)) {
95  // This time is so small that on a MSX you can
96  // never see BUSY=1. So I also couldn't test
97  // whether this timing applies to registers 3-6
98  // (like for write) or only to register 6. I
99  // also couldn't test how the other registers
100  // behave.
101  // TODO Should we comment out this code? It
102  // doesn't have any measurable effect on MSX.
103  ymf278BusyTime = time + MEM_READ_DELAY;
104  }
105  result = ymf278.readReg(opl4latch);
106  break;
107  default: // unreachable, avoid warning
108  UNREACHABLE; result = 255;
109  }
110  } else {
111  // FM part 0xC4-0xC7
112  switch (port & 0x03) {
113  case 0: // read status
114  case 2:
115  result = ymf262.readStatus() |
116  readYMF278Status(time);
117  if (!alreadyReadID && getNew2()) {
118  // Verified on real YMF278:
119  // Only once after switching NEW2=1, reading
120  // the status register returns '0x02'. This
121  // behavior doesn't re-occur till after a
122  // reset (datasheet confirms this behavior).
123  // Also verified that only bit 1 changes (so
124  // it's not the whole value that is forced to
125  // 0x02, datasheet isn't clear about that).
126  alreadyReadID = true;
127  result |= 0x02;
128  }
129  break;
130  case 1:
131  case 3: // read fm register
132  result = ymf262.readReg(opl3latch);
133  break;
134  default: // unreachable, avoid warning
135  UNREACHABLE; result = 255;
136  }
137  }
138  return result;
139 }
140 
141 byte MSXMoonSound::peekIO(word port, EmuTime::param time) const
142 {
143  byte result;
144  if ((port & 0xFF) < 0xC0) {
145  // WAVE part 0x7E-0x7F
146  switch (port & 0x01) {
147  case 0: // read latch, not supported
148  result = 255;
149  break;
150  case 1: // read wave register
151  result = ymf278.peekReg(opl4latch);
152  break;
153  default: // unreachable, avoid warning
154  UNREACHABLE; result = 255;
155  }
156  } else {
157  // FM part 0xC4-0xC7
158  switch (port & 0x03) {
159  case 0: // read status
160  case 2:
161  result = ymf262.peekStatus() |
162  readYMF278Status(time);
163  if (!alreadyReadID && getNew2()) {
164  result |= 0x02;
165  }
166  break;
167  case 1:
168  case 3: // read fm register
169  result = ymf262.peekReg(opl3latch);
170  break;
171  default: // unreachable, avoid warning
172  UNREACHABLE; result = 255;
173  }
174  }
175  return result;
176 }
177 
178 void MSXMoonSound::writeIO(word port, byte value, EmuTime::param time)
179 {
180  if ((port & 0xFF) < 0xC0) {
181  // WAVE part 0x7E-0x7F
182  if (getNew2()) {
183  switch (port & 0x01) {
184  case 0: // select register
185  ymf278BusyTime = time + WAVE_REG_SELECT_DELAY;
186  opl4latch = value;
187  break;
188  case 1:
189  if ((0x08 <= opl4latch) && (opl4latch <= 0x1F)) {
190  ymf278LoadTime = time + LOAD_DELAY;
191  }
192  if ((3 <= opl4latch) && (opl4latch <= 6)) {
193  // Note: this time is so small that on
194  // MSX you never see BUSY=1 for these
195  // registers. Confirmed on real HW that
196  // also registers 3-5 are faster.
197  ymf278BusyTime = time + MEM_WRITE_DELAY;
198  } else {
199  // For the other registers it is
200  // possible to see BUSY=1, but only
201  // very briefly and only on R800.
202  ymf278BusyTime = time + WAVE_REG_WRITE_DELAY;
203  }
204  if (opl4latch == 0xf8) {
205  ymf262.setMixLevel(value, time);
206  } else if (opl4latch == 0xf9) {
207  ymf278.setMixLevel(value, time);
208  }
209  ymf278.writeReg(opl4latch, value, time);
210  break;
211  default:
212  UNREACHABLE;
213  }
214  } else {
215  // Verified on real YMF278:
216  // Writes are ignored when NEW2=0 (both register select
217  // and register write).
218  }
219  } else {
220  // FM part 0xC4-0xC7
221  switch (port & 0x03) {
222  case 0: // select register bank 0
223  opl3latch = value;
224  ymf278BusyTime = time + FM_REG_SELECT_DELAY;
225  break;
226  case 2: // select register bank 1
227  opl3latch = value | 0x100;
228  ymf278BusyTime = time + FM_REG_SELECT_DELAY;
229  break;
230  case 1:
231  case 3: // write fm register
232  ymf278BusyTime = time + FM_REG_WRITE_DELAY;
233  ymf262.writeReg(opl3latch, value, time);
234  break;
235  default:
236  UNREACHABLE;
237  }
238  }
239 }
240 
241 bool MSXMoonSound::getNew2() const
242 {
243  return (ymf262.peekReg(0x105) & 0x02) != 0;
244 }
245 
246 byte MSXMoonSound::readYMF278Status(EmuTime::param time) const
247 {
248  byte result = 0;
249  if (time < ymf278BusyTime) result |= 0x01;
250  if (time < ymf278LoadTime) result |= 0x02;
251  return result;
252 }
253 
254 // version 1: initial version
255 // version 2: added alreadyReadID
256 // version 3: moved loadTime and busyTime from YMF278 to here
257 template<typename Archive>
258 void MSXMoonSound::serialize(Archive& ar, unsigned version)
259 {
260  ar.template serializeBase<MSXDevice>(*this);
261  ar.serialize("ymf262", ymf262);
262  ar.serialize("ymf278", ymf278);
263  ar.serialize("opl3latch", opl3latch);
264  ar.serialize("opl4latch", opl4latch);
265  if (ar.versionAtLeast(version, 2)) {
266  ar.serialize("alreadyReadID", alreadyReadID);
267  } else {
268  assert(ar.isLoader());
269  alreadyReadID = true; // we can't know the actual value, but
270  // 'true' is the safest value
271  }
272  if (ar.versionAtLeast(version, 3)) {
273  ar.serialize("loadTime", ymf278LoadTime);
274  ar.serialize("busyTime", ymf278BusyTime);
275  } else {
276  assert(ar.isLoader());
277  // For 100% backwards compatibility we should restore these two
278  // from the (old) YMF278 class. Though that's a lot of extra
279  // work for very little gain.
280  ymf278LoadTime = getCurrentTime();
281  ymf278BusyTime = getCurrentTime();
282  }
283 }
285 REGISTER_MSXDEVICE(MSXMoonSound, "MoonSound");
286 
287 } // namespace openmsx
void clearRam()
Definition: YMF278.cc:830
void setMixLevel(uint8_t x, EmuTime::param time)
Definition: YMF262.cc:1534
byte readReg(byte reg)
Definition: YMF278.cc:749
byte peekStatus() const
Definition: YMF262.cc:1514
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
void writeReg(unsigned r, byte v, EmuTime::param time)
Definition: YMF262.cc:1062
void writeReg(byte reg, byte data, EmuTime::param time)
Definition: YMF278.cc:577
Represents a clock with a fixed frequency.
Definition: Clock.hh:18
byte peekReg(unsigned reg) const
Definition: YMF262.cc:1057
static EmuDuration duration(unsigned ticks)
Calculates the duration of the given number of ticks at this clock&#39;s frequency.
Definition: Clock.hh:35
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
byte readStatus()
Definition: YMF262.cc:1506
void setMixLevel(uint8_t x, EmuTime::param time)
Definition: YMF278.cc:476
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
Definition: MSXMoonSound.cc:80
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:133
void reset(EmuTime::param time)
Definition: YMF262.cc:1423
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX...
Definition: MSXDevice.hh:31
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
Definition: MSXMoonSound.cc:61
void reset(EmuTime::param time) override
This method is called on reset.
Definition: MSXMoonSound.cc:67
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:840
string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:589
void serialize(Archive &ar, unsigned version)
MSXMoonSound(const DeviceConfig &config)
Definition: MSXMoonSound.cc:49
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
byte readReg(unsigned reg)
Definition: YMF262.cc:1051
byte peekReg(byte reg) const
Definition: YMF278.cc:764
void writeIO(word port, byte value, EmuTime::param time) override
Write a byte to a given IO port at a certain time to this device.
void reset(EmuTime::param time)
Definition: YMF278.cc:835
#define UNREACHABLE
Definition: unreachable.hh:38