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