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