openMSX
ReproCartridgeV1.cc
Go to the documentation of this file.
1 #include "ReproCartridgeV1.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 1 is similar to Konami Ultimate Collection. It
13  uses the same flashROM, SCC/SCC+. But it only supports Konami SCC mapper.
14 
15  Released were cartridges with the following content (at least): only Metal
16  Gear, only Metal Gear 2
17 
18 [REGISTER (#7FFF)]
19 If it contains value 0x50, the flash is writable and the mapper is disabled.
20 Otherwise, the mapper is enabled and the flash is readonly.
21 
22 - Mapper supports 4 different ROMs of 2MB each, with the KonamiSCC mapper
23 - Cartridge has a PSG at 0x10, write only
24 - On I/O port 0x13 the 2MB block can be selected (default 0, so up to 3)
25 
26 ******************************************************************************/
27 
28 namespace openmsx {
29 
30 static std::vector<AmdFlash::SectorInfo> getSectorInfo()
31 {
32  std::vector<AmdFlash::SectorInfo> sectorInfo;
33  // 8 * 8kB
34  sectorInfo.insert(end(sectorInfo), 8, {8 * 1024, false});
35  // 127 * 64kB
36  sectorInfo.insert(end(sectorInfo), 127, {64 * 1024, false});
37  return sectorInfo;
38 }
39 
41  const DeviceConfig& config, Rom&& rom_)
42  : MSXRom(config, std::move(rom_))
43  , flash(rom, getSectorInfo(), 0x207E, true, config, false)
44  , scc("MGCV1 SCC", config, getCurrentTime(), SCC::SCC_Compatible)
45  , psg("MGCV1 PSG", DummyAY8910Periphery::instance(), config,
46  getCurrentTime())
47 {
49 
50  getCPUInterface().register_IO_Out(0x10, this);
51  getCPUInterface().register_IO_Out(0x11, this);
52  getCPUInterface().register_IO_Out(0x13, this);
53 }
54 
56 {
57  getCPUInterface().unregister_IO_Out(0x10, this);
58  getCPUInterface().unregister_IO_Out(0x11, this);
59  getCPUInterface().unregister_IO_Out(0x13, this);
60 }
61 
62 void ReproCartridgeV1::powerUp(EmuTime::param time)
63 {
64  scc.powerUp(time);
65  reset(time);
66 }
67 
68 void ReproCartridgeV1::reset(EmuTime::param time)
69 {
70  flashRomWriteEnabled = false;
71  mainBankReg = 0;
72  sccMode = 0;
73  for (int bank = 0; bank < 4; ++bank) {
74  bankRegs[bank] = bank;
75  }
76 
77  scc.reset(time);
78  psgLatch = 0;
79  psg.reset(time);
80 
81  flash.reset();
82 
83  invalidateDeviceRCache(); // flush all to be sure
84 }
85 
86 unsigned ReproCartridgeV1::getFlashAddr(unsigned addr) const
87 {
88  unsigned page8kB = (addr >> 13) - 2;
89  if (page8kB >= 4) return unsigned(-1); // outside [0x4000, 0xBFFF]
90 
91  byte bank = bankRegs[page8kB]; // 2MB max
92  return (mainBankReg << 21) | (bank << 13) | (addr & 0x1FFF);
93 }
94 
95 // Note: implementation (mostly) copied from KUC
96 bool ReproCartridgeV1::isSCCAccess(word addr) const
97 {
98  if (sccMode & 0x10) return false;
99 
100  if (addr & 0x0100) {
101  // Address bit 8 must be zero, this is different from a real
102  // SCC/SCC+. According to Manuel Pazos this is a leftover from
103  // an earlier version that had 2 SCCs: the SCC on the left or
104  // right channel reacts when address bit 8 is respectively 0/1.
105  return false;
106  }
107 
108  if (sccMode & 0x20) {
109  // SCC+ range: 0xB800..0xBFFF, excluding 0xBFFE-0xBFFF
110  return (bankRegs[3] & 0x80) && (0xB800 <= addr) && (addr < 0xBFFE);
111  } else {
112  // SCC range: 0x9800..0x9FFF, excluding 0x9FFE-0x9FFF
113  return ((bankRegs[2] & 0x3F) == 0x3F) && (0x9800 <= addr) && (addr < 0x9FFE);
114  }
115 }
116 
117 byte ReproCartridgeV1::readMem(word addr, EmuTime::param time)
118 {
119  if (isSCCAccess(addr)) {
120  return scc.readMem(addr & 0xFF, time);
121  }
122 
123  unsigned flashAddr = getFlashAddr(addr);
124  return (flashAddr != unsigned(-1))
125  ? flash.read(flashAddr)
126  : 0xFF; // unmapped read
127 }
128 
129 byte ReproCartridgeV1::peekMem(word addr, EmuTime::param time) const
130 {
131  if (isSCCAccess(addr)) {
132  return scc.peekMem(addr & 0xFF, time);
133  }
134 
135  unsigned flashAddr = getFlashAddr(addr);
136  return (flashAddr != unsigned(-1))
137  ? flash.peek(flashAddr)
138  : 0xFF; // unmapped read
139 }
140 
142 {
143  if (isSCCAccess(addr)) return nullptr;
144 
145  unsigned flashAddr = getFlashAddr(addr);
146  return (flashAddr != unsigned(-1))
147  ? flash.getReadCacheLine(flashAddr)
148  : unmappedRead;
149 }
150 
151 void ReproCartridgeV1::writeMem(word addr, byte value, EmuTime::param time)
152 {
153  unsigned page8kB = (addr >> 13) - 2;
154  if (page8kB >= 4) return; // outside [0x4000, 0xBFFF]
155 
156  // There are several overlapping functional regions in the address
157  // space. A single write can trigger behaviour in multiple regions. In
158  // other words there's no priority amongst the regions where a higher
159  // priority region blocks the write from the lower priority regions.
160  // This only goes for places where the flash is 'seen', so not for the
161  // SCC registers
162 
163  if (isSCCAccess(addr)) {
164  scc.writeMem(addr & 0xFF, value, time);
165  return; // write to SCC blocks write to other functions
166  }
167 
168  // address is calculated before writes to other regions take effect
169  unsigned flashAddr = getFlashAddr(addr);
170 
171  // Main mapper register
172  if (addr == 0x7FFF) {
173  flashRomWriteEnabled = (value == 0x50);
174  invalidateDeviceRCache(); // flush all to be sure
175  }
176 
177  if (!flashRomWriteEnabled) {
178  // Konami-SCC
179  if ((addr & 0x1800) == 0x1000) {
180  // [0x5000,0x57FF] [0x7000,0x77FF]
181  // [0x9000,0x97FF] [0xB000,0xB7FF]
182  bankRegs[page8kB] = value;
183  invalidateDeviceRCache(0x4000 + 0x2000 * page8kB, 0x2000);
184  }
185 
186  // SCC mode register
187  if ((addr & 0xFFFE) == 0xBFFE) {
188  sccMode = value;
189  scc.setChipMode((value & 0x20) ? SCC::SCC_plusmode
191  invalidateDeviceRCache(0x9800, 0x800);
192  invalidateDeviceRCache(0xB800, 0x800);
193  }
194  } else {
195  if (flashAddr != unsigned(-1)) {
196  flash.write(flashAddr, value);
197  }
198  }
199 }
200 
202 {
203  return ((0x4000 <= addr) && (addr < 0xC000))
204  ? nullptr // [0x4000,0xBFFF] isn't cacheable
205  : unmappedWrite;
206 }
207 
208 void ReproCartridgeV1::writeIO(word port, byte value, EmuTime::param time)
209 {
210  switch (port & 0xFF)
211  {
212  case 0x10:
213  psgLatch = value & 0x0F;
214  break;
215  case 0x11:
216  psg.writeRegister(psgLatch, value, time);
217  break;
218  case 0x13:
219  mainBankReg = value & 3;
220  invalidateDeviceRCache(); // flush all to be sure
221  break;
222  default: UNREACHABLE;
223  }
224 }
225 
226 template<typename Archive>
227 void ReproCartridgeV1::serialize(Archive& ar, unsigned /*version*/)
228 {
229  // skip MSXRom base class
230  ar.template serializeBase<MSXDevice>(*this);
231 
232  ar.serialize("flash", flash,
233  "scc", scc,
234  "psg", psg,
235  "psgLatch", psgLatch,
236  "flashRomWriteEnabled", flashRomWriteEnabled,
237  "mainBankReg", mainBankReg,
238  "sccMode", sccMode,
239  "bankRegs", bankRegs);
240 }
242 REGISTER_MSXDEVICE(ReproCartridgeV1, "ReproCartridgeV1");
243 
244 } // namespace openmsx
void setChipMode(ChipMode newMode)
Definition: SCC.cc:182
byte read(unsigned address)
Definition: AmdFlash.cc:252
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
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
STL namespace.
void reset(EmuTime::param time)
Definition: SCC.cc:172
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 reset(EmuTime::param time) override
This method is called on reset.
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
ReproCartridgeV1(const DeviceConfig &config, Rom &&rom)
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 peek(unsigned address) const
Definition: AmdFlash.cc:215
byte readMem(byte address, EmuTime::param time)
Definition: SCC.cc:192
const byte * getReadCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading...
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:292
void register_IO_Out(byte port, MSXDevice *device)
Devices can register their Out ports.
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:130
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
byte * getWriteCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing...
byte peekMem(byte address, EmuTime::param time) const
Definition: SCC.cc:205
void unregister_IO_Out(byte port, MSXDevice *device)
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:293
void write(unsigned address, byte value)
Definition: AmdFlash.cc:270
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:981
MSXCPUInterface & getCPUInterface() const
Definition: MSXDevice.cc:138
void powerUp(EmuTime::param time)
Definition: SCC.cc:140
void reset(EmuTime::param time)
Definition: AY8910.cc:537
void serialize(Archive &ar, unsigned version)
void writeRegister(unsigned reg, byte value, EmuTime::param time)
Definition: AY8910.cc:594
void invalidateDeviceRCache()
Definition: MSXDevice.hh:209
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
#define UNREACHABLE
Definition: unreachable.hh:38