openMSX
ReproCartridgeV1.cc
Go to the documentation of this file.
1#include "ReproCartridgeV1.hh"
3#include "MSXCPUInterface.hh"
4#include "narrow.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)]
20If it contains value 0x50, the flash is writable and the mapper is disabled.
21Otherwise, 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
29namespace openmsx {
30
31static constexpr auto sectorInfo = [] {
32 // 8 * 8kB, followed by 127 * 64kB
33 using Info = AmdFlash::SectorInfo;
34 std::array<Info, 8 + 127> result = {};
35 std::fill(result.begin(), result.begin() + 8, Info{ 8 * 1024, false});
36 std::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 auto& cpuInterface = getCPUInterface();
51 for (auto port : {0x10, 0x11, 0x13}) {
52 cpuInterface.register_IO_Out(narrow_cast<byte>(port), this);
53 }
54}
55
57{
58 auto& cpuInterface = getCPUInterface();
59 for (auto port : {0x10, 0x11, 0x13}) {
60 cpuInterface.unregister_IO_Out(narrow_cast<byte>(port), this);
61 }
62}
63
64void ReproCartridgeV1::powerUp(EmuTime::param time)
65{
66 scc.powerUp(time);
67 reset(time);
68}
69
70void ReproCartridgeV1::reset(EmuTime::param time)
71{
72 flashRomWriteEnabled = false;
73 mainBankReg = 0;
74 sccMode = 0;
75 ranges::iota(bankRegs, byte(0));
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
86unsigned 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
96bool 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
117byte ReproCartridgeV1::readMem(word addr, EmuTime::param time)
118{
119 if (isSCCAccess(addr)) {
120 return scc.readMem(narrow_cast<uint8_t>(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
129byte ReproCartridgeV1::peekMem(word addr, EmuTime::param time) const
130{
131 if (isSCCAccess(addr)) {
132 return scc.peekMem(narrow_cast<uint8_t>(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.data();
149}
150
151void 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(narrow_cast<uint8_t>(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.data();
206}
207
208void 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
226template<typename Archive>
227void 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}
243
244} // namespace openmsx
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition MSXDevice.hh:354
void reset(EmuTime::param time)
Definition AY8910.cc:510
void writeRegister(unsigned reg, uint8_t value, EmuTime::param time)
Definition AY8910.cc:567
void write(size_t address, uint8_t value)
Definition AmdFlash.cc:265
const uint8_t * getReadCacheLine(size_t address) const
Definition AmdFlash.cc:254
uint8_t peek(size_t address) const
Definition AmdFlash.cc:212
uint8_t read(size_t address) const
Definition AmdFlash.cc:248
void invalidateDeviceRCache()
Definition MSXDevice.hh:213
static std::array< byte, 0x10000 > unmappedRead
Definition MSXDevice.hh:304
static std::array< byte, 0x10000 > unmappedWrite
Definition MSXDevice.hh:305
EmuTime::param getCurrentTime() const
Definition MSXDevice.cc:125
MSXCPUInterface & getCPUInterface() const
Definition MSXDevice.cc:133
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 setChipMode(ChipMode newMode)
Definition SCC.cc:183
void powerUp(EmuTime::param time)
Definition SCC.cc:141
@ SCC_plusmode
Definition SCC.hh:16
@ SCC_Compatible
Definition SCC.hh:16
uint8_t readMem(uint8_t address, EmuTime::param time)
Definition SCC.cc:193
void reset(EmuTime::param time)
Definition SCC.cc:173
uint8_t peekMem(uint8_t address, EmuTime::param time) const
Definition SCC.cc:206
void writeMem(uint8_t address, uint8_t value, EmuTime::param time)
Definition SCC.cc:285
This file implemented 3 utility functions:
Definition Autofire.cc:11
AmdFlash::SectorInfo Info
Definition RomManbow2.cc:18
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
constexpr void iota(ForwardIt first, ForwardIt last, T value)
Definition ranges.hh:312
STL namespace.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define UNREACHABLE