openMSX
KonamiUltimateCollection.cc
Go to the documentation of this file.
2 #include "serialize.hh"
3 #include <vector>
4 
5 /******************************************************************************
6  * DOCUMENTATION AS PROVIDED BY MANUEL PAZOS, WHO DEVELOPED THE CARTRIDGE *
7  ******************************************************************************
8 
9  Konami Ultimate Collection shares some features of MegaFlashROM SCC+ SD
10  It uses the same flashROM, SCC/SCC+, Konami and Konami SCC mappers, etc.
11 
12 [OFFSET REGISTER (#7FFE)]
13 7-0: Bank offset
14 
15 [MAPPER REGISTER (#7FFF)]
16 7 A21 \
17 6 A20 / FlashROM address lines to switch 2 MB banks.
18 5 Mapper mode : Select Konami mapper (0=SCC or 1=normal). [1]
19 4 Flash write enable
20 3 Disable #4000-#5FFF mapper in Konami mode, Enable DAC (works like the DAC of Synthesizer or Majutsushi)
21 2 Disable mapper register
22 1 Disable mapper (bank switching)
23 0 no function anymore (was mapper limits)
24 
25 [1] bit 5 only changes the address range of the mapper (Konami or Konami SCC)
26 but the SCC is always available. This feature is inherited from MFC SCC+ subslot
27 mode because all subslots share the same mapper type. So to make Konami
28 combinations (i.e.: Konami ROM with Konami SCC ROM) a "common mapper" is needed
29 and the SCC must be available. So I made a "Konami mapper" with SCC. In fact,
30 all Konami and Konami SCC ROMs should work with "Konami" mapper in KUC.
31 
32 ******************************************************************************/
33 
34 namespace openmsx {
35 
36 static std::vector<AmdFlash::SectorInfo> getSectorInfo()
37 {
38  std::vector<AmdFlash::SectorInfo> sectorInfo;
39  // 8 * 8kB
40  sectorInfo.insert(end(sectorInfo), 8, {8 * 1024, false});
41  // 127 * 64kB
42  sectorInfo.insert(end(sectorInfo), 127, {64 * 1024, false});
43  return sectorInfo;
44 }
45 
47  const DeviceConfig& config, Rom&& rom_)
48  : MSXRom(config, std::move(rom_))
49  , flash(rom, getSectorInfo(), 0x207E, true, config)
50  , scc("KUC SCC", config, getCurrentTime(), SCC::SCC_Compatible)
51  , dac("KUC DAC", "Konami Ultimate Collection DAC", config)
52 {
54 }
55 
56 void KonamiUltimateCollection::powerUp(EmuTime::param time)
57 {
58  scc.powerUp(time);
59  reset(time);
60 }
61 
62 void KonamiUltimateCollection::reset(EmuTime::param time)
63 {
64  mapperReg = 0;
65  offsetReg = 0;
66  sccMode = 0;
67  for (int bank = 0; bank < 4; ++bank) {
68  bankRegs[bank] = bank;
69  }
70 
71  scc.reset(time);
72  dac.reset(time);
73 
74  invalidateMemCache(0x0000, 0x10000); // 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  invalidateMemCache(0x0000, 0x10000); // 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  invalidateMemCache(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  invalidateMemCache(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  invalidateMemCache(0x9800, 0x800);
209  invalidateMemCache(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 reset(EmuTime::param time) override
This method is called on reset.
const byte * getReadCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading...
void serialize(Archive &ar, unsigned version)
void setChipMode(ChipMode newMode)
Definition: SCC.cc:182
byte read(unsigned address)
Definition: AmdFlash.cc:252
void writeDAC(uint8_t value, EmuTime::param time)
Definition: DACSound8U.cc:16
void writeMem(byte address, byte value, EmuTime::param time)
Definition: SCC.cc:289
const byte * getReadCacheLine(unsigned address) const
Definition: AmdFlash.cc:258
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
STL namespace.
void reset(EmuTime::param time)
Definition: SCC.cc:172
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
byte * getWriteCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing...
byte peek(unsigned address) const
Definition: AmdFlash.cc:215
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
byte readMem(byte address, EmuTime::param time)
Definition: SCC.cc:192
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:133
KonamiUltimateCollection(const DeviceConfig &config, Rom &&rom)
void reset(EmuTime::param time)
Definition: DACSound16S.cc:29
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
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.
byte peekMem(byte address, EmuTime::param time) const
Definition: SCC.cc:205
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:274
void write(unsigned address, byte value)
Definition: AmdFlash.cc:270
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1006
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:275
void powerUp(EmuTime::param time)
Definition: SCC.cc:140
void invalidateMemCache(word start, unsigned size)
Invalidate CPU memory-mapping cache.
Definition: MSXDevice.cc:458
auto end(const string_view &x)
Definition: string_view.hh:152