OXYGENHUB
◆ main openmsx / src / memory / KonamiUltimateCollection.cc
7.3 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "KonamiUltimateCollection.hh"
2
3
#include "serialize.hh"
4
5
#include "narrow.hh"
6
#include "outer.hh"
7
#include "ranges.hh"
8
9
#include <array>
10
11
/******************************************************************************
12
 * DOCUMENTATION AS PROVIDED BY MANUEL PAZOS, WHO DEVELOPED THE CARTRIDGE     *
13
 ******************************************************************************
14
15
   Konami Ultimate Collection shares some features of MegaFlashROM SCC+ SD
16
   It uses the same flashROM, SCC/SCC+, Konami and Konami SCC mappers, etc.
17
18
[OFFSET REGISTER (#7FFE)]
19
7-0: Bank offset
20
21
[MAPPER REGISTER (#7FFF)]
22
7     A21 \
23
6     A20 / FlashROM address lines to switch 2 MB banks.
24
5     Mapper mode  :   Select Konami mapper (0=SCC or 1=normal). [1]
25
4     Flash write enable
26
3     Disable #4000-#5FFF mapper in Konami mode, Enable DAC (works like the DAC of Synthesizer or Majutsushi)
27
2     Disable mapper register
28
1     Disable mapper (bank switching)
29
0     no function anymore (was mapper limits)
30
31
[1] bit 5 only changes the address range of the mapper (Konami or Konami SCC)
32
but the SCC is always available.  This feature is inherited from MFC SCC+ subslot
33
mode because all subslots share the same mapper type. So to make Konami
34
combinations (i.e.: Konami ROM with Konami SCC ROM) a "common mapper" is needed
35
and the SCC must be available. So I made a "Konami mapper" with SCC.  In fact,
36
all Konami and Konami SCC ROMs should work with "Konami" mapper in KUC.
37
38
******************************************************************************/
39
40
namespace openmsx {
41
42
KonamiUltimateCollection::KonamiUltimateCollection(DeviceConfig& config, Rom&& rom_)
43
	: MSXRom(config, std::move(rom_))
44
	, romBlockDebug(*this)
45
	, flash(rom, AmdFlashChip::M29W640GB, {}, config)
46
	, scc("KUC SCC", config, getCurrentTime(), SCC::Mode::Compatible)
47
	, dac("KUC DAC", "Konami Ultimate Collection DAC", config)
48
{
49
	powerUp(getCurrentTime());
50
}
51
52
void KonamiUltimateCollection::powerUp(EmuTime time)
53
{
54
	scc.powerUp(time);
55
	reset(time);
56
}
57
58
void KonamiUltimateCollection::reset(EmuTime time)
59
{
60
	mapperReg = 0;
61
	offsetReg = 0;
62
	sccMode = 0;
63
	ranges::iota(bankRegs, byte(0));
64
65
	scc.reset(time);
66
	dac.reset(time);
67
68
	invalidateDeviceRWCache(); // flush all to be sure
69
}
70
71
unsigned KonamiUltimateCollection::getFlashAddr(unsigned addr) const
72
{
73
	unsigned page8kB = (addr >> 13) - 2;
74
	if (page8kB >= 4) return unsigned(-1); // outside [0x4000, 0xBFFF]
75
76
	byte bank = bankRegs[page8kB] + offsetReg; // wrap at 8 bit
77
	return ((mapperReg & 0xC0) << (21 - 6)) | (bank << 13) | (addr & 0x1FFF);
78
}
79
80
bool KonamiUltimateCollection::isSCCAccess(uint16_t addr) const
81
{
82
	if (sccMode & 0x10) return false;
83
84
	if (addr & 0x0100) {
85
		// Address bit 8 must be zero, this is different from a real
86
		// SCC/SCC+. According to Manuel Pazos this is a leftover from
87
		// an earlier version that had 2 SCCs: the SCC on the left or
88
		// right channel reacts when address bit 8 is respectively 0/1.
89
		return false;
90
	}
91
92
	if (sccMode & 0x20) {
93
		// SCC+   range: 0xB800..0xBFFF,  excluding 0xBFFE-0xBFFF
94
		return  (bankRegs[3] & 0x80)          && (0xB800 <= addr) && (addr < 0xBFFE);
95
	} else {
96
		// SCC    range: 0x9800..0x9FFF,  excluding 0x9FFE-0x9FFF
97
		return ((bankRegs[2] & 0x3F) == 0x3F) && (0x9800 <= addr) && (addr < 0x9FFE);
98
	}
99
}
100
101
byte KonamiUltimateCollection::readMem(uint16_t addr, EmuTime time)
102
{
103
	if (isSCCAccess(addr)) {
104
		return scc.readMem(narrow_cast<uint8_t>(addr & 0xFF), time);
105
	}
106
107
	unsigned flashAddr = getFlashAddr(addr);
108
	return (flashAddr != unsigned(-1))
109
		? flash.read(flashAddr, time)
110
		: 0xFF; // unmapped read
111
}
112
113
byte KonamiUltimateCollection::peekMem(uint16_t addr, EmuTime time) const
114
{
115
	if (isSCCAccess(addr)) {
116
		return scc.peekMem(narrow_cast<uint8_t>(addr & 0xFF), time);
117
	}
118
119
	unsigned flashAddr = getFlashAddr(addr);
120
	return (flashAddr != unsigned(-1))
121
		? flash.peek(flashAddr, time)
122
		: 0xFF; // unmapped read
123
}
124
125
const byte* KonamiUltimateCollection::getReadCacheLine(uint16_t addr) const
126
{
127
	if (isSCCAccess(addr)) return nullptr;
128
129
	unsigned flashAddr = getFlashAddr(addr);
130
	return (flashAddr != unsigned(-1))
131
		? flash.getReadCacheLine(flashAddr)
132
		: unmappedRead.data();
133
}
134
135
void KonamiUltimateCollection::writeMem(uint16_t addr, byte value, EmuTime time)
136
{
137
	unsigned page8kB = (addr >> 13) - 2;
138
	if (page8kB >= 4) return; // outside [0x4000, 0xBFFF]
139
140
	// There are several overlapping functional regions in the address
141
	// space. A single write can trigger behaviour in multiple regions. In
142
	// other words there's no priority amongst the regions where a higher
143
	// priority region blocks the write from the lower priority regions.
144
	// This only goes for places where the flash is 'seen', so not for the
145
	// SCC registers
146
147
	if (isSCCAccess(addr)) {
148
		scc.writeMem(narrow_cast<uint8_t>(addr & 0xFF), value, time);
149
		return; // write to SCC blocks write to other functions
150
	}
151
152
	// address is calculated before writes to other regions take effect
153
	unsigned flashAddr = getFlashAddr(addr);
154
155
	// Mapper and offset registers
156
	if (isMapperRegisterEnabled()) {
157
		if (addr == 0x7FFF) {
158
			mapperReg = value;
159
		} else if (addr == 0x7FFE) {
160
			offsetReg = value;
161
		}
162
		invalidateDeviceRCache(); // flush all to be sure
163
	}
164
165
166
	// DAC
167
	if (isBank0Disabled() && (addr < 0x6000) && ((addr & 0x0010) == 0)) {
168
		dac.writeDAC(value, time);
169
	}
170
171
	if (areBankRegsEnabled()) {
172
		// Bank-switching
173
		if (isKonamiSCCmode()) {
174
			// Konami-SCC
175
			if ((addr & 0x1800) == 0x1000) {
176
				// [0x5000,0x57FF] [0x7000,0x77FF]
177
				// [0x9000,0x97FF] [0xB000,0xB7FF]
178
				// Masking of the mapper bits is done on write
179
				bankRegs[page8kB] = value;
180
				invalidateDeviceRCache(0x4000 + 0x2000 * page8kB, 0x2000);
181
			}
182
		} else {
183
			// Konami
184
			if (isBank0Disabled() && (addr < 0x6000)) {
185
				// Switching 0x4000-0x5FFF disabled (only Konami mode).
186
			} else {
187
				// [0x5000,0x57FF] asymmetric!!!
188
				// [0x6000,0x7FFF] [0x8000,0x9FFF] [0xA000,0xBFFF]
189
				if (!((addr < 0x5000) || ((0x5800 <= addr) && (addr < 0x6000)))) {
190
					// Masking of the mapper bits is done on write
191
					bankRegs[page8kB] = value;
192
					invalidateDeviceRCache(0x4000 + 0x2000 * page8kB, 0x2000);
193
				}
194
			}
195
		}
196
197
		// SCC mode register
198
		if ((addr & 0xFFFE) == 0xBFFE) {
199
			sccMode = value;
200
			scc.setMode((value & 0x20) ? SCC::Mode::Plus
201
			                           : SCC::Mode::Compatible);
202
			invalidateDeviceRCache(0x9800, 0x800);
203
			invalidateDeviceRCache(0xB800, 0x800);
204
		}
205
	}
206
207
	if ((flashAddr != unsigned(-1)) && isFlashRomWriteEnabled()) {
208
		flash.write(flashAddr, value, time);
209
	}
210
}
211
212
byte* KonamiUltimateCollection::getWriteCacheLine(uint16_t addr)
213
{
214
	return ((0x4000 <= addr) && (addr < 0xC000))
215
	       ? nullptr        // [0x4000,0xBFFF] isn't cacheable
216
	       : unmappedWrite.data();
217
}
218
219
template<typename Archive>
220
void KonamiUltimateCollection::serialize(Archive& ar, unsigned /*version*/)
221
{
222
	// skip MSXRom base class
223
	ar.template serializeBase<MSXDevice>(*this);
224
225
	ar.serialize("flash",     flash,
226
	             "scc",       scc,
227
	             "DAC",       dac,
228
	             "mapperReg", mapperReg,
229
	             "offsetReg", offsetReg,
230
	             "sccMode",   sccMode,
231
	             "bankRegs",  bankRegs);
232
}
233
INSTANTIATE_SERIALIZE_METHODS(KonamiUltimateCollection);
234
REGISTER_MSXDEVICE(KonamiUltimateCollection, "KonamiUltimateCollection");
235
236
237
unsigned KonamiUltimateCollection::Blocks::readExt(unsigned address)
238
{
239
	const auto& dev = OUTER(KonamiUltimateCollection, romBlockDebug);
240
	return dev.getFlashAddr(address) >> 13;
241
}
242
243
} // namespace openmsx