OXYGENHUB
◆ main openmsx / src / sound / YMF278B.cc
7.2 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "YMF278B.hh"
2
3
#include "Clock.hh"
4
#include "serialize.hh"
5
6
#include "unreachable.hh"
7
8
namespace openmsx {
9
10
// The master clock, running at 33.8MHz.
11
using MasterClock = Clock<33868800>;
12
13
// Required delay between register select and register read/write.
14
static constexpr auto FM_REG_SELECT_DELAY = MasterClock::duration(56);
15
// Required delay after register write.
16
static 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.
22
static constexpr auto WAVE_REG_SELECT_DELAY = MasterClock::duration(88);
23
// Required delay after register write.
24
static 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.
30
static constexpr auto MEM_READ_DELAY  = MasterClock::duration(38);
31
// Required delay after memory write (instead of register write delay).
32
static 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).
39
static constexpr auto LOAD_DELAY = MasterClock::duration(10000);
40
41
YMF278B::YMF278B(const std::string& name, size_t ramSize, DeviceConfig& config,
42
                 YMF278::SetupMemPtrFunc setupMemPtrs, EmuTime 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
51
void YMF278B::powerUp(EmuTime time)
52
{
53
	ymf278.clearRam();
54
	reset(time);
55
}
56
57
void YMF278B::reset(EmuTime 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
69
uint8_t YMF278B::readIO(uint16_t port, EmuTime 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:
94
			UNREACHABLE;
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:
106
			UNREACHABLE;
107
		}
108
	}
109
}
110
111
uint8_t YMF278B::peekIO(uint16_t port, EmuTime 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:
121
			UNREACHABLE;
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:
133
			UNREACHABLE;
134
		}
135
	}
136
}
137
138
void YMF278B::writeIO(uint16_t port, uint8_t value, EmuTime 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:
172
				UNREACHABLE;
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:
196
			UNREACHABLE;
197
		}
198
	}
199
}
200
201
bool YMF278B::getNew2() const
202
{
203
	return (ymf262.peekReg(0x105) & 0x02) != 0;
204
}
205
206
uint8_t YMF278B::readYMF278Status(EmuTime time) const
207
{
208
	uint8_t result = 0;
209
	if (time < ymf278BusyTime) result |= 0x01;
210
	if (time < ymf278LoadTime) result |= 0x02;
211
	return result;
212
}
213
214
void YMF278B::setupMemoryPointers()
215
{
216
	ymf278.setupMemoryPointers();
217
}
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
224
void YMF278B::serialize_bw_compat(XmlInputArchive& ar, unsigned version, EmuTime 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 {
234
		assert(XmlInputArchive::IS_LOADER);
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
244
template<typename Archive>
245
void 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
}
254
INSTANTIATE_SERIALIZE_METHODS(YMF278B);
255
256
} // namespace openmsx