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