openMSX
ReproCartridgeV2.cc
Go to the documentation of this file.
1 #include "ReproCartridgeV2.hh"
3 #include "MSXCPUInterface.hh"
4 #include "serialize.hh"
5 #include <vector>
6 
7 
8 /******************************************************************************
9  * DOCUMENTATION AS PROVIDED BY MANUEL PAZOS, WHO DEVELOPED THE CARTRIDGE *
10  ******************************************************************************
11 
12  Repro Cartridge version 2 is similar to Konami Ultimate Collection. It
13  uses the same flashROM, SCC/SCC+. But 2 PSG's and volume control and
14  multiple mappers.
15 
16  Released were cartridge with the following content: Only Metal Gear, only
17  Metal Gear 2, Vampire Killer (original, Castlevania patch, enhanced SCC),
18  Usas (original, enhanced), Nemesis 3 (enhanced) and Metal Gear Collection
19  (Metal Gear 1 and 2, both in 3 languages)
20 
21 
22 [REGISTER (#7FFE)]
23 Selects the mapper, using bits 0 and 1 (other bits are ignored):
24  00 = Konami SCC
25  01 = Konami
26  10 = ASCII8
27  11 = ASCII16
28 
29 [REGISTER (#7FFF)]
30 If it contains value 0x50, the flash is writable and the mapper is disabled.
31 Otherwise, the mapper is enabled and the flash is readonly.
32 
33 - Mapper supports 8 different ROMs of 1MB each, with the above mappers.
34 - Cartridge has a PSG at 0x10 and a PSG at 0xA0, both write only
35 - On I/O port 0x33 the 1MB block can be selected (default 0, so up to 7)
36 - On I/O port 0x31 the volume can be selected of the 3 sound chips:
37  Bits EEIIISSS: EE(7-6) = PSG#10, III(5-3) = PSG#A0, SSS(2-0) = SCC
38  Default value is &B10000010. 0 means silent. So, clone PSG is silent by
39  default.
40 
41 Note: there is also a version 1 of this hardware, with the following
42 differences:
43 - Only Konami SCC mapper (no register at #7FFE)
44 - No volume control register behind port #31
45 - Main bank register is behind port #13 instead of #33
46 - Main block size is 2MB instead of 1MB
47 - No extra PSG at 0xA0 (but the PSG at #10 is there)
48 
49 ******************************************************************************/
50 
51 namespace openmsx {
52 
53 static std::vector<AmdFlash::SectorInfo> getSectorInfo()
54 {
55  std::vector<AmdFlash::SectorInfo> sectorInfo;
56  // 8 * 8kB
57  sectorInfo.insert(end(sectorInfo), 8, {8 * 1024, false});
58  // 127 * 64kB
59  sectorInfo.insert(end(sectorInfo), 127, {64 * 1024, false});
60  return sectorInfo;
61 }
62 
64  const DeviceConfig& config, Rom&& rom_)
65  : MSXRom(config, std::move(rom_))
66  , flash(rom, getSectorInfo(), 0x207E, true, config, false)
67  , scc("MGCV2 SCC", config, getCurrentTime(), SCC::SCC_Compatible)
68  , psg0x10("MGCV2 PSG@0x10", DummyAY8910Periphery::instance(), config,
69  getCurrentTime())
70  , psg0xA0("MGCV2 PSG@0xA0", DummyAY8910Periphery::instance(), config,
71  getCurrentTime())
72 {
74 
75  getCPUInterface().register_IO_Out(0x10, this);
76  getCPUInterface().register_IO_Out(0x11, this);
77  getCPUInterface().register_IO_Out(0x31, this);
78  getCPUInterface().register_IO_Out(0x33, this);
79  getCPUInterface().register_IO_Out(0xA0, this);
80  getCPUInterface().register_IO_Out(0xA1, this);
81 }
82 
84 {
85  getCPUInterface().unregister_IO_Out(0x10, this);
86  getCPUInterface().unregister_IO_Out(0x11, this);
87  getCPUInterface().unregister_IO_Out(0x31, this);
88  getCPUInterface().unregister_IO_Out(0x33, this);
89  getCPUInterface().unregister_IO_Out(0xA0, this);
90  getCPUInterface().unregister_IO_Out(0xA1, this);
91 }
92 
93 void ReproCartridgeV2::powerUp(EmuTime::param time)
94 {
95  scc.powerUp(time);
96  reset(time);
97 }
98 
99 void ReproCartridgeV2::reset(EmuTime::param time)
100 {
101  flashRomWriteEnabled = false;
102  mainBankReg = 0;
103  setVolume(time, 0b10'000'10);
104  mapperTypeReg = 0;
105  sccMode = 0;
106  for (int bank = 0; bank < 4; ++bank) {
107  bankRegs[bank] = bank;
108  }
109 
110  scc.reset(time);
111  psg0x10Latch = 0;
112  psg0x10.reset(time);
113  psg0xA0Latch = 0;
114  psg0xA0.reset(time);
115 
116  flash.reset();
117 
118  invalidateMemCache(0x0000, 0x10000); // flush all to be sure
119 }
120 
121 unsigned ReproCartridgeV2::getFlashAddr(unsigned addr) const
122 {
123  unsigned page8kB = (addr >> 13) - 2;
124  if (page8kB >= 4) return unsigned(-1); // outside [0x4000, 0xBFFF]
125 
126  byte bank = bankRegs[page8kB] & 127; // 1MB max
127  return (mainBankReg << 20) | (bank << 13) | (addr & 0x1FFF);
128 }
129 
130 // Note: implementation (mostly) copied from KUC
131 bool ReproCartridgeV2::isSCCAccess(word addr) const
132 {
133  if ((mapperTypeReg != 0) || (sccMode & 0x10)) return false;
134 
135  if (addr & 0x0100) {
136  // Address bit 8 must be zero, this is different from a real
137  // SCC/SCC+. According to Manuel Pazos this is a leftover from
138  // an earlier version that had 2 SCCs: the SCC on the left or
139  // right channel reacts when address bit 8 is respectively 0/1.
140  return false;
141  }
142 
143  if (sccMode & 0x20) {
144  // SCC+ range: 0xB800..0xBFFF, excluding 0xBFFE-0xBFFF
145  return (bankRegs[3] & 0x80) && (0xB800 <= addr) && (addr < 0xBFFE);
146  } else {
147  // SCC range: 0x9800..0x9FFF, excluding 0x9FFE-0x9FFF
148  return ((bankRegs[2] & 0x3F) == 0x3F) && (0x9800 <= addr) && (addr < 0x9FFE);
149  }
150 }
151 
152 byte ReproCartridgeV2::readMem(word addr, EmuTime::param time)
153 {
154  if (isSCCAccess(addr)) {
155  return scc.readMem(addr & 0xFF, time);
156  }
157 
158  unsigned flashAddr = getFlashAddr(addr);
159  return (flashAddr != unsigned(-1))
160  ? flash.read(flashAddr)
161  : 0xFF; // unmapped read
162 }
163 
164 byte ReproCartridgeV2::peekMem(word addr, EmuTime::param time) const
165 {
166  if (isSCCAccess(addr)) {
167  return scc.peekMem(addr & 0xFF, time);
168  }
169 
170  unsigned flashAddr = getFlashAddr(addr);
171  return (flashAddr != unsigned(-1))
172  ? flash.peek(flashAddr)
173  : 0xFF; // unmapped read
174 }
175 
177 {
178  if (isSCCAccess(addr)) return nullptr;
179 
180  unsigned flashAddr = getFlashAddr(addr);
181  return (flashAddr != unsigned(-1))
182  ? flash.getReadCacheLine(flashAddr)
183  : unmappedRead;
184 }
185 
186 void ReproCartridgeV2::writeMem(word addr, byte value, EmuTime::param time)
187 {
188  unsigned page8kB = (addr >> 13) - 2;
189  if (page8kB >= 4) return; // outside [0x4000, 0xBFFF]
190 
191  // There are several overlapping functional regions in the address
192  // space. A single write can trigger behaviour in multiple regions. In
193  // other words there's no priority amongst the regions where a higher
194  // priority region blocks the write from the lower priority regions.
195  // This only goes for places where the flash is 'seen', so not for the
196  // SCC registers
197 
198  if (isSCCAccess(addr)) {
199  scc.writeMem(addr & 0xFF, value, time);
200  return; // write to SCC blocks write to other functions
201  }
202 
203  // address is calculated before writes to other regions take effect
204  unsigned flashAddr = getFlashAddr(addr);
205 
206  // Main mapper register
207  if (addr == 0x7FFF) {
208  flashRomWriteEnabled = (value == 0x50);
209  invalidateMemCache(0x0000, 0x10000); // flush all to be sure
210  }
211  // Mapper selection register
212  if (addr == 0x7FFE) {
213  mapperTypeReg = value & 3; // other bits are ignored, so no need to store
214  invalidateMemCache(0x0000, 0x10000); // flush all to be sure
215  }
216 
217  if (!flashRomWriteEnabled) {
218  switch (mapperTypeReg) {
219  case 0:
220  // Konami-SCC
221  if ((addr & 0x1800) == 0x1000) {
222  // [0x5000,0x57FF] [0x7000,0x77FF]
223  // [0x9000,0x97FF] [0xB000,0xB7FF]
224  bankRegs[page8kB] = value;
225  invalidateMemCache(0x4000 + 0x2000 * page8kB, 0x2000);
226  }
227 
228  // SCC mode register
229  if ((addr & 0xFFFE) == 0xBFFE) {
230  sccMode = value;
231  scc.setChipMode((value & 0x20) ? SCC::SCC_plusmode
233  invalidateMemCache(0x9800, 0x800);
234  invalidateMemCache(0xB800, 0x800);
235  }
236  break;
237  case 1:
238  {
239  // Konami
240  // (Copied from MegaFlashROMSCCPlus)
241  // Masking of the mapper bits is done on
242  // write (and only in Konami(-scc) mode)
243  if ((addr < 0x5000) || ((0x5800 <= addr) && (addr < 0x6000))) break; // only SCC range works
244  bankRegs[page8kB] = value & 0x7F;
245  invalidateMemCache(0x4000 + 0x2000 * page8kB, 0x2000);
246  break;
247  }
248  case 2:
249  // ASCII-8
250  // (Copied from MegaFlashROMSCCPlus)
251  if ((0x6000 <= addr) && (addr < 0x8000)) {
252  byte bank = (addr >> 11) & 0x03;
253  bankRegs[bank] = value;
254  invalidateMemCache(0x4000 + 0x2000 * bank, 0x2000);
255  }
256  break;
257  case 3:
258  // ASCII-16
259  // (Copied from MegaFlashROMSCCPlus)
260  // This behaviour is confirmed by Manuel Pazos (creator
261  // of the cartridge): ASCII-16 uses all 4 bank registers
262  // and one bank switch changes 2 registers at once.
263  // This matters when switching mapper mode, because
264  // the content of the bank registers is unchanged after
265  // a switch.
266  if ((0x6000 <= addr) && (addr < 0x6800)) {
267  bankRegs[0] = 2 * value + 0;
268  bankRegs[1] = 2 * value + 1;
269  invalidateMemCache(0x4000, 0x4000);
270  }
271  if ((0x7000 <= addr) && (addr < 0x7800)) {
272  bankRegs[2] = 2 * value + 0;
273  bankRegs[3] = 2 * value + 1;
274  invalidateMemCache(0x8000, 0x4000);
275  }
276  break;
277  default:
278  UNREACHABLE;
279  }
280  } else {
281  if (flashAddr != unsigned(-1)) {
282  flash.write(flashAddr, value);
283  }
284  }
285 }
286 
288 {
289  return ((0x4000 <= addr) && (addr < 0xC000))
290  ? nullptr // [0x4000,0xBFFF] isn't cacheable
291  : unmappedWrite;
292 }
293 
294 void ReproCartridgeV2::writeIO(word port, byte value, EmuTime::param time)
295 {
296  switch (port & 0xFF)
297  {
298  case 0x10:
299  psg0x10Latch = value & 0x0F;
300  break;
301  case 0x11:
302  psg0x10.writeRegister(psg0x10Latch, value, time);
303  break;
304  case 0xA0:
305  psg0xA0Latch = value & 0x0F;
306  break;
307  case 0xA1:
308  psg0xA0.writeRegister(psg0xA0Latch, value, time);
309  break;
310  case 0x31:
311  setVolume(time, value);
312  break;
313  case 0x33:
314  mainBankReg = value & 7;
315  invalidateMemCache(0x0000, 0x10000); // flush all to be sure
316  break;
317  default: UNREACHABLE;
318  }
319 }
320 
321 void ReproCartridgeV2::setVolume(EmuTime::param time, byte value)
322 {
323  // store (mostly for the save/loadstate feature)
324  volumeReg = value;
325  // EE (7-6) = PSG#10. So values 0-3
326  // III(5-3) = PSG#A0. So values 0-7
327  // SSS(2-0) = SCC. So values 0-7
328  scc .setSoftwareVolume(((volumeReg >> 0) & 7) / 2.0f, time);
329  psg0xA0.setSoftwareVolume(((volumeReg >> 3) & 7) / 2.0f, time);
330  psg0x10.setSoftwareVolume(((volumeReg >> 6) & 3) / 2.0f, time);
331 }
332 
333 template<typename Archive>
334 void ReproCartridgeV2::serialize(Archive& ar, unsigned /*version*/)
335 {
336  // skip MSXRom base class
337  ar.template serializeBase<MSXDevice>(*this);
338 
339  ar.serialize("flash", flash);
340  ar.serialize("scc", scc);
341  ar.serialize("psg0x10", psg0x10);
342  ar.serialize("psg0x10Latch", psg0x10Latch);
343  ar.serialize("psg0xA0", psg0xA0);
344  ar.serialize("psg0xA0Latch", psg0xA0Latch);
345  ar.serialize("flashRomWriteEnabled", flashRomWriteEnabled);
346  ar.serialize("mainBankReg", mainBankReg);
347  ar.serialize("volumeReg", volumeReg);
348  ar.serialize("mapperTypeReg", mapperTypeReg);
349  ar.serialize("sccMode", sccMode);
350  ar.serialize("bankRegs", bankRegs);
351 
352  if (ar.isLoader())
353  {
354  auto time = getCurrentTime();
355  setVolume(time, volumeReg);
356  }
357 
358 }
360 REGISTER_MSXDEVICE(ReproCartridgeV2, "ReproCartridgeV2");
361 
362 } // namespace openmsx
void setSoftwareVolume(float volume, EmuTime::param time)
Change the &#39;software volume&#39; of this sound device.
Definition: SoundDevice.cc:140
void setChipMode(ChipMode newMode)
Definition: SCC.cc:182
byte read(unsigned address)
Definition: AmdFlash.cc:252
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
void writeMem(byte address, byte value, EmuTime::param time)
Definition: SCC.cc:289
const byte * getReadCacheLine(unsigned address) const
Definition: AmdFlash.cc:258
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.
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
ReproCartridgeV2(const DeviceConfig &config, Rom &&rom)
STL namespace.
void reset(EmuTime::param time)
Definition: SCC.cc:172
const byte * getReadCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading...
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
byte peek(unsigned address) const
Definition: AmdFlash.cc:215
void setVolume(EmuTime::param time, byte value)
byte readMem(byte address, EmuTime::param time)
Definition: SCC.cc:192
void reset(EmuTime::param time) override
This method is called on reset.
void register_IO_Out(byte port, MSXDevice *device)
Devices can register their Out ports.
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:133
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
byte peekMem(byte address, EmuTime::param time) const
Definition: SCC.cc:205
void unregister_IO_Out(byte port, MSXDevice *device)
void writeIO(word port, byte value, EmuTime::param time) override
Write a byte to a given IO port at a certain time to this device.
void serialize(Archive &ar, unsigned version)
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:840
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:275
MSXCPUInterface & getCPUInterface() const
Definition: MSXDevice.cc:141
byte * getWriteCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing...
void powerUp(EmuTime::param time)
Definition: SCC.cc:140
void reset(EmuTime::param time)
Definition: AY8910.cc:539
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
void writeRegister(unsigned reg, byte value, EmuTime::param time)
Definition: AY8910.cc:596
#define UNREACHABLE
Definition: unreachable.hh:38