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