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