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
39 const DeviceConfig& config, Rom&& rom_)
40 : MSXRom(config, std::move(rom_))
41 , flash(rom, AmdFlashChip::M29W640GB, {}, config)
42 , scc("KUC SCC", config, getCurrentTime(), SCC::Mode::Compatible)
43 , dac("KUC DAC", "Konami Ultimate Collection DAC", config)
44{
46}
47
48void KonamiUltimateCollection::powerUp(EmuTime::param time)
49{
50 scc.powerUp(time);
51 reset(time);
52}
53
54void KonamiUltimateCollection::reset(EmuTime::param time)
55{
56 mapperReg = 0;
57 offsetReg = 0;
58 sccMode = 0;
59 ranges::iota(bankRegs, byte(0));
60
61 scc.reset(time);
62 dac.reset(time);
63
64 invalidateDeviceRWCache(); // flush all to be sure
65}
66
67unsigned KonamiUltimateCollection::getFlashAddr(unsigned addr) const
68{
69 unsigned page8kB = (addr >> 13) - 2;
70 if (page8kB >= 4) return unsigned(-1); // outside [0x4000, 0xBFFF]
71
72 byte bank = bankRegs[page8kB] + offsetReg; // wrap at 8 bit
73 return ((mapperReg & 0xC0) << (21 - 6)) | (bank << 13) | (addr & 0x1FFF);
74}
75
76bool KonamiUltimateCollection::isSCCAccess(word addr) const
77{
78 if (sccMode & 0x10) return false;
79
80 if (addr & 0x0100) {
81 // Address bit 8 must be zero, this is different from a real
82 // SCC/SCC+. According to Manuel Pazos this is a leftover from
83 // an earlier version that had 2 SCCs: the SCC on the left or
84 // right channel reacts when address bit 8 is respectively 0/1.
85 return false;
86 }
87
88 if (sccMode & 0x20) {
89 // SCC+ range: 0xB800..0xBFFF, excluding 0xBFFE-0xBFFF
90 return (bankRegs[3] & 0x80) && (0xB800 <= addr) && (addr < 0xBFFE);
91 } else {
92 // SCC range: 0x9800..0x9FFF, excluding 0x9FFE-0x9FFF
93 return ((bankRegs[2] & 0x3F) == 0x3F) && (0x9800 <= addr) && (addr < 0x9FFE);
94 }
95}
96
97byte KonamiUltimateCollection::readMem(word addr, EmuTime::param time)
98{
99 if (isSCCAccess(addr)) {
100 return scc.readMem(narrow_cast<uint8_t>(addr & 0xFF), time);
101 }
102
103 unsigned flashAddr = getFlashAddr(addr);
104 return (flashAddr != unsigned(-1))
105 ? flash.read(flashAddr)
106 : 0xFF; // unmapped read
107}
108
109byte KonamiUltimateCollection::peekMem(word addr, EmuTime::param time) const
110{
111 if (isSCCAccess(addr)) {
112 return scc.peekMem(narrow_cast<uint8_t>(addr & 0xFF), time);
113 }
114
115 unsigned flashAddr = getFlashAddr(addr);
116 return (flashAddr != unsigned(-1))
117 ? flash.peek(flashAddr)
118 : 0xFF; // unmapped read
119}
120
122{
123 if (isSCCAccess(addr)) return nullptr;
124
125 unsigned flashAddr = getFlashAddr(addr);
126 return (flashAddr != unsigned(-1))
127 ? flash.getReadCacheLine(flashAddr)
128 : unmappedRead.data();
129}
130
131void KonamiUltimateCollection::writeMem(word addr, byte value, EmuTime::param time)
132{
133 unsigned page8kB = (addr >> 13) - 2;
134 if (page8kB >= 4) return; // outside [0x4000, 0xBFFF]
135
136 // There are several overlapping functional regions in the address
137 // space. A single write can trigger behaviour in multiple regions. In
138 // other words there's no priority amongst the regions where a higher
139 // priority region blocks the write from the lower priority regions.
140 // This only goes for places where the flash is 'seen', so not for the
141 // SCC registers
142
143 if (isSCCAccess(addr)) {
144 scc.writeMem(narrow_cast<uint8_t>(addr & 0xFF), value, time);
145 return; // write to SCC blocks write to other functions
146 }
147
148 // address is calculated before writes to other regions take effect
149 unsigned flashAddr = getFlashAddr(addr);
150
151 // Mapper and offset registers
152 if (isMapperRegisterEnabled()) {
153 if (addr == 0x7FFF) {
154 mapperReg = value;
155 } else if (addr == 0x7FFE) {
156 offsetReg = value;
157 }
158 invalidateDeviceRCache(); // flush all to be sure
159 }
160
161
162 // DAC
163 if (isBank0Disabled() && (addr < 0x6000) && ((addr & 0x0010) == 0)) {
164 dac.writeDAC(value, time);
165 }
166
167 if (areBankRegsEnabled()) {
168 // Bank-switching
169 if (isKonamiSCCmode()) {
170 // Konami-SCC
171 if ((addr & 0x1800) == 0x1000) {
172 // [0x5000,0x57FF] [0x7000,0x77FF]
173 // [0x9000,0x97FF] [0xB000,0xB7FF]
174 // Masking of the mapper bits is done on write
175 bankRegs[page8kB] = value;
176 invalidateDeviceRCache(0x4000 + 0x2000 * page8kB, 0x2000);
177 }
178 } else {
179 // Konami
180 if (isBank0Disabled() && (addr < 0x6000)) {
181 // Switching 0x4000-0x5FFF disabled (only Konami mode).
182 } else {
183 // [0x5000,0x57FF] asymmetric!!!
184 // [0x6000,0x7FFF] [0x8000,0x9FFF] [0xA000,0xBFFF]
185 if (!((addr < 0x5000) || ((0x5800 <= addr) && (addr < 0x6000)))) {
186 // Masking of the mapper bits is done on write
187 bankRegs[page8kB] = value;
188 invalidateDeviceRCache(0x4000 + 0x2000 * page8kB, 0x2000);
189 }
190 }
191 }
192
193 // SCC mode register
194 if ((addr & 0xFFFE) == 0xBFFE) {
195 sccMode = value;
196 scc.setMode((value & 0x20) ? SCC::Mode::Plus
198 invalidateDeviceRCache(0x9800, 0x800);
199 invalidateDeviceRCache(0xB800, 0x800);
200 }
201 }
202
203 if ((flashAddr != unsigned(-1)) && isFlashRomWriteEnabled()) {
204 flash.write(flashAddr, value);
205 }
206}
207
209{
210 return ((0x4000 <= addr) && (addr < 0xC000))
211 ? nullptr // [0x4000,0xBFFF] isn't cacheable
212 : unmappedWrite.data();
213}
214
215template<typename Archive>
216void KonamiUltimateCollection::serialize(Archive& ar, unsigned /*version*/)
217{
218 // skip MSXRom base class
219 ar.template serializeBase<MSXDevice>(*this);
220
221 ar.serialize("flash", flash,
222 "scc", scc,
223 "DAC", dac,
224 "mapperReg", mapperReg,
225 "offsetReg", offsetReg,
226 "sccMode", sccMode,
227 "bankRegs", bankRegs);
228}
230REGISTER_MSXDEVICE(KonamiUltimateCollection, "KonamiUltimateCollection");
231
232} // namespace openmsx
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition MSXDevice.hh:356
void write(size_t address, uint8_t value)
Definition AmdFlash.cc:466
const uint8_t * getReadCacheLine(size_t address) const
Definition AmdFlash.cc:455
uint8_t read(size_t address)
Definition AmdFlash.cc:444
uint8_t peek(size_t address) const
Definition AmdFlash.cc:206
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:314
STL namespace.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)