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 
75  ymf278BusyTime = time;
76  ymf278LoadTime = time;
77 }
78 
79 byte MSXMoonSound::readIO(word port, EmuTime::param time)
80 {
81  byte result;
82  if ((port & 0xFF) < 0xC0) {
83  // WAVE part 0x7E-0x7F
84  switch (port & 0x01) {
85  case 0: // read latch, not supported
86  result = 255;
87  break;
88  case 1: // read wave register
89  // Verified on real YMF278:
90  // Even if NEW2=0 reads happen normally. Also reads
91  // from sample memory (and thus the internal memory
92  // pointer gets increased).
93  if ((3 <= opl4latch) && (opl4latch <= 6)) {
94  // This time is so small that on a MSX you can
95  // never see BUSY=1. So I also couldn't test
96  // whether this timing applies to registers 3-6
97  // (like for write) or only to register 6. I
98  // also couldn't test how the other registers
99  // behave.
100  // TODO Should we comment out this code? It
101  // doesn't have any measurable effect on MSX.
102  ymf278BusyTime = time + MEM_READ_DELAY;
103  }
104  result = ymf278.readReg(opl4latch);
105  break;
106  default: // unreachable, avoid warning
107  UNREACHABLE; result = 255;
108  }
109  } else {
110  // FM part 0xC4-0xC7
111  switch (port & 0x03) {
112  case 0: // read status
113  case 2:
114  result = ymf262.readStatus() | readYMF278Status(time);
115  break;
116  case 1:
117  case 3: // read fm register
118  result = ymf262.readReg(opl3latch);
119  break;
120  default: // unreachable, avoid warning
121  UNREACHABLE; result = 255;
122  }
123  }
124  return result;
125 }
126 
127 byte MSXMoonSound::peekIO(word port, EmuTime::param time) const
128 {
129  byte result;
130  if ((port & 0xFF) < 0xC0) {
131  // WAVE part 0x7E-0x7F
132  switch (port & 0x01) {
133  case 0: // read latch, not supported
134  result = 255;
135  break;
136  case 1: // read wave register
137  result = ymf278.peekReg(opl4latch);
138  break;
139  default: // unreachable, avoid warning
140  UNREACHABLE; result = 255;
141  }
142  } else {
143  // FM part 0xC4-0xC7
144  switch (port & 0x03) {
145  case 0: // read status
146  case 2:
147  result = ymf262.peekStatus() | readYMF278Status(time);
148  break;
149  case 1:
150  case 3: // read fm register
151  result = ymf262.peekReg(opl3latch);
152  break;
153  default: // unreachable, avoid warning
154  UNREACHABLE; result = 255;
155  }
156  }
157  return result;
158 }
159 
160 void MSXMoonSound::writeIO(word port, byte value, EmuTime::param time)
161 {
162  if ((port & 0xFF) < 0xC0) {
163  // WAVE part 0x7E-0x7F
164  if (getNew2()) {
165  switch (port & 0x01) {
166  case 0: // select register
167  ymf278BusyTime = time + WAVE_REG_SELECT_DELAY;
168  opl4latch = value;
169  break;
170  case 1:
171  if ((0x08 <= opl4latch) && (opl4latch <= 0x1F)) {
172  ymf278LoadTime = time + LOAD_DELAY;
173  }
174  if ((3 <= opl4latch) && (opl4latch <= 6)) {
175  // Note: this time is so small that on
176  // MSX you never see BUSY=1 for these
177  // registers. Confirmed on real HW that
178  // also registers 3-5 are faster.
179  ymf278BusyTime = time + MEM_WRITE_DELAY;
180  } else {
181  // For the other registers it is
182  // possible to see BUSY=1, but only
183  // very briefly and only on R800.
184  ymf278BusyTime = time + WAVE_REG_WRITE_DELAY;
185  }
186  if (opl4latch == 0xf8) {
187  ymf262.setMixLevel(value, time);
188  } else if (opl4latch == 0xf9) {
189  ymf278.setMixLevel(value, time);
190  }
191  ymf278.writeReg(opl4latch, value, time);
192  break;
193  default:
194  UNREACHABLE;
195  }
196  } else {
197  // Verified on real YMF278:
198  // Writes are ignored when NEW2=0 (both register select
199  // and register write).
200  }
201  } else {
202  // FM part 0xC4-0xC7
203  switch (port & 0x03) {
204  case 0: // select register bank 0
205  opl3latch = value;
206  ymf278BusyTime = time + FM_REG_SELECT_DELAY;
207  break;
208  case 2: // select register bank 1
209  opl3latch = value | 0x100;
210  ymf278BusyTime = time + FM_REG_SELECT_DELAY;
211  break;
212  case 1:
213  case 3: // write fm register
214  ymf278BusyTime = time + FM_REG_WRITE_DELAY;
215  ymf262.writeReg(opl3latch, value, time);
216  break;
217  default:
218  UNREACHABLE;
219  }
220  }
221 }
222 
223 bool MSXMoonSound::getNew2() const
224 {
225  return (ymf262.peekReg(0x105) & 0x02) != 0;
226 }
227 
228 byte MSXMoonSound::readYMF278Status(EmuTime::param time) const
229 {
230  byte result = 0;
231  if (time < ymf278BusyTime) result |= 0x01;
232  if (time < ymf278LoadTime) result |= 0x02;
233  return result;
234 }
235 
236 // version 1: initial version
237 // version 2: added alreadyReadID
238 // version 3: moved loadTime and busyTime from YMF278 to here
239 // removed alreadyReadID
240 template<typename Archive>
241 void MSXMoonSound::serialize(Archive& ar, unsigned version)
242 {
243  ar.template serializeBase<MSXDevice>(*this);
244  ar.serialize("ymf262", ymf262,
245  "ymf278", ymf278,
246  "opl3latch", opl3latch,
247  "opl4latch", opl4latch);
248  if (ar.versionAtLeast(version, 3)) {
249  ar.serialize("loadTime", ymf278LoadTime,
250  "busyTime", ymf278BusyTime);
251  } else {
252  assert(ar.isLoader());
253  // For 100% backwards compatibility we should restore these two
254  // from the (old) YMF278 class. Though that's a lot of extra
255  // work for very little gain.
256  ymf278LoadTime = getCurrentTime();
257  ymf278BusyTime = getCurrentTime();
258  }
259 }
261 REGISTER_MSXDEVICE(MSXMoonSound, "MoonSound");
262 
263 } // namespace openmsx
void clearRam()
Definition: YMF278.cc:831
void setMixLevel(uint8_t x, EmuTime::param time)
Definition: YMF262.cc:1532
byte readReg(byte reg)
Definition: YMF278.cc:750
byte peekStatus() const
Definition: YMF262.cc:1512
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:578
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:1504
void setMixLevel(uint8_t x, EmuTime::param time)
Definition: YMF278.cc:477
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:79
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:133
void reset(EmuTime::param time)
Definition: YMF262.cc:1421
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:1006
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:765
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:836
#define UNREACHABLE
Definition: unreachable.hh:38