openMSX
KonamiUltimateCollection.cc
Go to the documentation of this file.
2 #include "cstd.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)]
15 7-0: Bank offset
16 
17 [MAPPER REGISTER (#7FFF)]
18 7 A21 \
19 6 A20 / FlashROM address lines to switch 2 MB banks.
20 5 Mapper mode : Select Konami mapper (0=SCC or 1=normal). [1]
21 4 Flash write enable
22 3 Disable #4000-#5FFF mapper in Konami mode, Enable DAC (works like the DAC of Synthesizer or Majutsushi)
23 2 Disable mapper register
24 1 Disable mapper (bank switching)
25 0 no function anymore (was mapper limits)
26 
27 [1] bit 5 only changes the address range of the mapper (Konami or Konami SCC)
28 but the SCC is always available. This feature is inherited from MFC SCC+ subslot
29 mode because all subslots share the same mapper type. So to make Konami
30 combinations (i.e.: Konami ROM with Konami SCC ROM) a "common mapper" is needed
31 and the SCC must be available. So I made a "Konami mapper" with SCC. In fact,
32 all Konami and Konami SCC ROMs should work with "Konami" mapper in KUC.
33 
34 ******************************************************************************/
35 
36 namespace openmsx {
37 
38 static constexpr auto sectorInfo = [] {
39  // 8 * 8kB, followed by 127 * 64kB
40  using Info = AmdFlash::SectorInfo;
41  std::array<Info, 8 + 127> result = {};
42  cstd::fill(result.begin(), result.begin() + 8, Info{ 8 * 1024, false});
43  cstd::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 
58 void KonamiUltimateCollection::powerUp(EmuTime::param time)
59 {
60  scc.powerUp(time);
61  reset(time);
62 }
63 
64 void KonamiUltimateCollection::reset(EmuTime::param time)
65 {
66  mapperReg = 0;
67  offsetReg = 0;
68  sccMode = 0;
69  ranges::iota(bankRegs, 0);
70 
71  scc.reset(time);
72  dac.reset(time);
73 
74  invalidateDeviceRWCache(); // flush all to be sure
75 }
76 
77 unsigned 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 
86 bool 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 
107 byte KonamiUltimateCollection::readMem(word addr, EmuTime::param time)
108 {
109  if (isSCCAccess(addr)) {
110  return scc.readMem(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 
119 byte KonamiUltimateCollection::peekMem(word addr, EmuTime::param time) const
120 {
121  if (isSCCAccess(addr)) {
122  return scc.peekMem(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;
139 }
140 
141 void 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(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;
223 }
224 
225 template<typename Archive>
226 void 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 }
240 REGISTER_MSXDEVICE(KonamiUltimateCollection, "KonamiUltimateCollection");
241 
242 } // namespace openmsx
void write(unsigned address, byte value)
Definition: AmdFlash.cc:268
byte peek(unsigned address) const
Definition: AmdFlash.cc:215
byte read(unsigned address) const
Definition: AmdFlash.cc:251
const byte * getReadCacheLine(unsigned address) const
Definition: AmdFlash.cc:257
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:209
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:300
void invalidateDeviceRWCache()
Calls MSXCPUInterface::invalidateXXCache() for the specific (part of) the slot that this device is lo...
Definition: MSXDevice.hh:208
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:132
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:301
void writeMem(byte address, byte value, EmuTime::param time)
Definition: SCC.cc:287
void setChipMode(ChipMode newMode)
Definition: SCC.cc:185
void powerUp(EmuTime::param time)
Definition: SCC.cc:143
byte readMem(byte address, EmuTime::param time)
Definition: SCC.cc:195
@ SCC_plusmode
Definition: SCC.hh:14
@ SCC_Compatible
Definition: SCC.hh:14
void reset(EmuTime::param time)
Definition: SCC.cc:175
byte peekMem(byte address, EmuTime::param time) const
Definition: SCC.cc:208
constexpr void fill(ForwardIt first, ForwardIt last, const T &value)
Definition: cstd.hh:111
This file implemented 3 utility functions:
Definition: Autofire.cc:5
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
AmdFlash::SectorInfo Info
Definition: RomManbow2.cc:17
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
constexpr void iota(ForwardIt first, ForwardIt last, T value)
Definition: ranges.hh:204
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:983