openMSX
YMF278B.cc
Go to the documentation of this file.
1#include "YMF278B.hh"
2
3#include "Clock.hh"
4#include "serialize.hh"
5
6#include "unreachable.hh"
7
8namespace openmsx {
9
10// The master clock, running at 33.8MHz.
12
13// Required delay between register select and register read/write.
14static constexpr auto FM_REG_SELECT_DELAY = MasterClock::duration(56);
15// Required delay after register write.
16static constexpr auto FM_REG_WRITE_DELAY = MasterClock::duration(56);
17// Datasheet doesn't mention any delay for reads from the FM registers. In fact
18// it says reads from FM registers are not possible while tests on a real
19// YMF278 show they do work (value of the NEW2 bit doesn't matter).
20
21// Required delay between register select and register read/write.
22static constexpr auto WAVE_REG_SELECT_DELAY = MasterClock::duration(88);
23// Required delay after register write.
24static constexpr auto WAVE_REG_WRITE_DELAY = MasterClock::duration(88);
25// Datasheet doesn't mention any delay for register reads (except for reads
26// from register 6, see below). I also couldn't measure any delay on a real
27// YMF278.
28
29// Required delay after memory read.
30static constexpr auto MEM_READ_DELAY = MasterClock::duration(38);
31// Required delay after memory write (instead of register write delay).
32static constexpr auto MEM_WRITE_DELAY = MasterClock::duration(28);
33
34// Required delay after instrument load.
35// We pick 10000 cycles, this is approximately 300us (the number given in the
36// datasheet). The exact number of cycles is unknown. But I did some (very
37// rough) tests on real HW, and this number is not too bad (slightly too high
38// but within 2%-4% of real value, needs more detailed tests).
39static constexpr auto LOAD_DELAY = MasterClock::duration(10000);
40
41YMF278B::YMF278B(const std::string& name, size_t ramSize, const DeviceConfig& config,
42 YMF278::SetupMemPtrFunc setupMemPtrs, EmuTime::param time)
43 : ymf262(name + " FM", config, true)
44 , ymf278(name + " wave", ramSize, config, std::move(setupMemPtrs))
45 , ymf278LoadTime(time)
46 , ymf278BusyTime(time)
47{
48 powerUp(time);
49}
50
51void YMF278B::powerUp(EmuTime::param time)
52{
53 ymf278.clearRam();
54 reset(time);
55}
56
57void YMF278B::reset(EmuTime::param time)
58{
59 ymf262.reset(time);
60 ymf278.reset(time);
61
62 opl4latch = 0; // TODO check
63 opl3latch = 0; // TODO check
64
65 ymf278BusyTime = time;
66 ymf278LoadTime = time;
67}
68
69byte YMF278B::readIO(word port, EmuTime::param time)
70{
71 if ((port & 0xFF) < 0xC0) {
72 // WAVE part 0x7E-0x7F
73 switch (port & 0x01) {
74 case 0: // read latch, not supported
75 return 255;
76 case 1: // read wave register
77 // Verified on real YMF278:
78 // Even if NEW2=0 reads happen normally. Also reads
79 // from sample memory (and thus the internal memory
80 // pointer gets increased).
81 if ((3 <= opl4latch) && (opl4latch <= 6)) {
82 // This time is so small that on a MSX you can
83 // never see BUSY=1. So I also couldn't test
84 // whether this timing applies to registers 3-6
85 // (like for write) or only to register 6. I
86 // also couldn't test how the other registers
87 // behave.
88 // TODO Should we comment out this code? It
89 // doesn't have any measurable effect on MSX.
90 ymf278BusyTime = time + MEM_READ_DELAY;
91 }
92 return ymf278.readReg(opl4latch);
93 default:
95 }
96 } else {
97 // FM part 0xC4-0xC7
98 switch (port & 0x03) {
99 case 0: // read status
100 case 2:
101 return ymf262.readStatus() | readYMF278Status(time);
102 case 1:
103 case 3: // read fm register
104 return ymf262.readReg(opl3latch);
105 default:
107 }
108 }
109}
110
111byte YMF278B::peekIO(word port, EmuTime::param time) const
112{
113 if ((port & 0xFF) < 0xC0) {
114 // WAVE part 0x7E-0x7F
115 switch (port & 0x01) {
116 case 0: // read latch, not supported
117 return 255;
118 case 1: // read wave register
119 return ymf278.peekReg(opl4latch);
120 default:
122 }
123 } else {
124 // FM part 0xC4-0xC7
125 switch (port & 0x03) {
126 case 0: // read status
127 case 2:
128 return ymf262.peekStatus() | readYMF278Status(time);
129 case 1:
130 case 3: // read fm register
131 return ymf262.peekReg(opl3latch);
132 default:
134 }
135 }
136}
137
138void YMF278B::writeIO(word port, byte value, EmuTime::param time)
139{
140 if ((port & 0xFF) < 0xC0) {
141 // WAVE part 0x7E-0x7F
142 if (getNew2()) {
143 switch (port & 0x01) {
144 case 0: // select register
145 ymf278BusyTime = time + WAVE_REG_SELECT_DELAY;
146 opl4latch = value;
147 break;
148 case 1:
149 if ((0x08 <= opl4latch) && (opl4latch <= 0x1F)) {
150 ymf278LoadTime = time + LOAD_DELAY;
151 }
152 if ((3 <= opl4latch) && (opl4latch <= 6)) {
153 // Note: this time is so small that on
154 // MSX you never see BUSY=1 for these
155 // registers. Confirmed on real HW that
156 // also registers 3-5 are faster.
157 ymf278BusyTime = time + MEM_WRITE_DELAY;
158 } else {
159 // For the other registers it is
160 // possible to see BUSY=1, but only
161 // very briefly and only on R800.
162 ymf278BusyTime = time + WAVE_REG_WRITE_DELAY;
163 }
164 if (opl4latch == 0xf8) {
165 ymf262.setMixLevel(value, time);
166 } else if (opl4latch == 0xf9) {
167 ymf278.setMixLevel(value, time);
168 }
169 ymf278.writeReg(opl4latch, value, time);
170 break;
171 default:
173 }
174 } else {
175 // Verified on real YMF278:
176 // Writes are ignored when NEW2=0 (both register select
177 // and register write).
178 }
179 } else {
180 // FM part 0xC4-0xC7
181 switch (port & 0x03) {
182 case 0: // select register bank 0
183 opl3latch = value;
184 ymf278BusyTime = time + FM_REG_SELECT_DELAY;
185 break;
186 case 2: // select register bank 1
187 opl3latch = value | 0x100;
188 ymf278BusyTime = time + FM_REG_SELECT_DELAY;
189 break;
190 case 1:
191 case 3: // write fm register
192 ymf278BusyTime = time + FM_REG_WRITE_DELAY;
193 ymf262.writeReg(opl3latch, value, time);
194 break;
195 default:
197 }
198 }
199}
200
201bool YMF278B::getNew2() const
202{
203 return (ymf262.peekReg(0x105) & 0x02) != 0;
204}
205
206byte YMF278B::readYMF278Status(EmuTime::param time) const
207{
208 byte result = 0;
209 if (time < ymf278BusyTime) result |= 0x01;
210 if (time < ymf278LoadTime) result |= 0x02;
211 return result;
212}
213
218
219// For backwards compatible savestates with (old) MSXMoonSound class.
220// version 1: initial version
221// version 2: added alreadyReadID
222// version 3: moved loadTime and busyTime from YMF278 to here
223// removed alreadyReadID
224void YMF278B::serialize_bw_compat(XmlInputArchive& ar, unsigned version, EmuTime::param time)
225{
226 ar.serialize("ymf262", ymf262,
227 "ymf278", ymf278,
228 "opl3latch", opl3latch,
229 "opl4latch", opl4latch);
230 if (ar.versionAtLeast(version, 3)) {
231 ar.serialize("loadTime", ymf278LoadTime,
232 "busyTime", ymf278BusyTime);
233 } else {
235 // For 100% backwards compatibility we should restore these two
236 // from the (old) YMF278 class. Though that's a lot of extra
237 // work for very little gain.
238 ymf278LoadTime = time;
239 ymf278BusyTime = time;
240 }
241}
242
243// version 1: initial version
244template<typename Archive>
245void YMF278B::serialize(Archive& ar, unsigned /*version*/)
246{
247 ar.serialize("ymf262", ymf262,
248 "ymf278", ymf278,
249 "opl3latch", opl3latch,
250 "opl4latch", opl4latch,
251 "loadTime", ymf278LoadTime,
252 "busyTime", ymf278BusyTime);
253}
255
256} // namespace openmsx
Represents a clock with a fixed frequency.
Definition Clock.hh:19
static constexpr EmuDuration duration(unsigned ticks)
Calculates the duration of the given number of ticks at this clock's frequency.
Definition Clock.hh:35
static constexpr bool IS_LOADER
Definition serialize.hh:525
bool versionAtLeast(unsigned actual, unsigned required) const
Definition serialize.hh:926
ALWAYS_INLINE void serialize(const char *tag, T &t, Args &&...args)
Definition serialize.hh:959
uint8_t readReg(unsigned reg) const
Definition YMF262.cc:1024
void reset(EmuTime::param time)
Definition YMF262.cc:1397
uint8_t peekStatus() const
Definition YMF262.cc:1481
void setMixLevel(uint8_t x, EmuTime::param time)
Definition YMF262.cc:1501
uint8_t readStatus()
Definition YMF262.cc:1473
void writeReg(unsigned r, uint8_t v, EmuTime::param time)
Definition YMF262.cc:1035
uint8_t peekReg(unsigned reg) const
Definition YMF262.cc:1030
void serialize(Archive &ar, unsigned version)
Definition YMF278B.cc:245
void setupMemoryPointers()
Definition YMF278B.cc:214
void writeIO(word port, byte value, EmuTime::param time)
Definition YMF278B.cc:138
void powerUp(EmuTime::param time)
Definition YMF278B.cc:51
YMF278B(const std::string &name, size_t ramSize, const DeviceConfig &config, YMF278::SetupMemPtrFunc setupMemPtrs, EmuTime::param time)
Definition YMF278B.cc:41
void serialize_bw_compat(XmlInputArchive &ar, unsigned version, EmuTime::param time)
Definition YMF278B.cc:224
byte readIO(word port, EmuTime::param time)
Definition YMF278B.cc:69
byte peekIO(word port, EmuTime::param time) const
Definition YMF278B.cc:111
void reset(EmuTime::param time)
Definition YMF278B.cc:57
uint8_t readReg(uint8_t reg)
Definition YMF278.cc:760
void writeReg(uint8_t reg, uint8_t data, EmuTime::param time)
Definition YMF278.cc:582
void setMixLevel(uint8_t x, EmuTime::param time)
Definition YMF278.cc:492
void reset(EmuTime::param time)
Definition YMF278.cc:830
void clearRam()
Definition YMF278.cc:825
void setupMemoryPointers()
Definition YMF278.cc:867
std::function< void(bool, std::span< const uint8_t >, std::span< const uint8_t >, std::span< YMF278::Block128, 32 >)> SetupMemPtrFunc
Definition YMF278.hh:34
uint8_t peekReg(uint8_t reg) const
Definition YMF278.cc:775
This file implemented 3 utility functions:
Definition Autofire.cc:11
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
STL namespace.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define UNREACHABLE