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  "scc", scc,
341  "psg0x10", psg0x10,
342  "psg0x10Latch", psg0x10Latch,
343  "psg0xA0", psg0xA0,
344  "psg0xA0Latch", psg0xA0Latch,
345  "flashRomWriteEnabled", flashRomWriteEnabled,
346  "mainBankReg", mainBankReg,
347  "volumeReg", volumeReg,
348  "mapperTypeReg", mapperTypeReg,
349  "sccMode", sccMode,
350  "bankRegs", bankRegs);
351 
352  if (ar.isLoader()) {
353  auto time = getCurrentTime();
354  setVolume(time, volumeReg);
355  }
356 
357 }
359 REGISTER_MSXDEVICE(ReproCartridgeV2, "ReproCartridgeV2");
360 
361 } // namespace openmsx
void setSoftwareVolume(float volume, EmuTime::param time)
Change the &#39;software volume&#39; of this sound device.
Definition: SoundDevice.cc:141
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:1006
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:537
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:594
#define UNREACHABLE
Definition: unreachable.hh:38