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
32 const DeviceConfig& config, Rom&& rom_)
33 : MSXRom(config, std::move(rom_))
34 , flash(rom, AmdFlashChip::M29W640GB, {}, config)
35 , scc("ReproCartV1 SCC", config, getCurrentTime(), SCC::Mode::Compatible)
36 , psg("ReproCartV1 PSG", DummyAY8910Periphery::instance(), config,
37 getCurrentTime())
38{
39 powerUp(getCurrentTime());
40 auto& cpuInterface = getCPUInterface();
41 for (auto port : {0x10, 0x11, 0x13}) {
42 cpuInterface.register_IO_Out(narrow_cast<byte>(port), this);
43 }
44}
45
47{
48 auto& cpuInterface = getCPUInterface();
49 for (auto port : {0x10, 0x11, 0x13}) {
50 cpuInterface.unregister_IO_Out(narrow_cast<byte>(port), this);
51 }
52}
53
54void ReproCartridgeV1::powerUp(EmuTime::param time)
55{
56 scc.powerUp(time);
57 reset(time);
58}
59
60void ReproCartridgeV1::reset(EmuTime::param time)
61{
62 flashRomWriteEnabled = false;
63 mainBankReg = 0;
64 sccMode = 0;
65 ranges::iota(bankRegs, byte(0));
66
67 scc.reset(time);
68 psgLatch = 0;
69 psg.reset(time);
70
71 flash.reset();
72
73 invalidateDeviceRCache(); // flush all to be sure
74}
75
76unsigned ReproCartridgeV1::getFlashAddr(unsigned addr) const
77{
78 unsigned page8kB = (addr >> 13) - 2;
79 if (page8kB >= 4) return unsigned(-1); // outside [0x4000, 0xBFFF]
80
81 byte bank = bankRegs[page8kB]; // 2MB max
82 return (mainBankReg << 21) | (bank << 13) | (addr & 0x1FFF);
83}
84
85// Note: implementation (mostly) copied from KUC
86bool ReproCartridgeV1::isSCCAccess(word addr) const
87{
88 if (sccMode & 0x10) return false;
89
90 if (addr & 0x0100) {
91 // Address bit 8 must be zero, this is different from a real
92 // SCC/SCC+. According to Manuel Pazos this is a leftover from
93 // an earlier version that had 2 SCCs: the SCC on the left or
94 // right channel reacts when address bit 8 is respectively 0/1.
95 return false;
96 }
97
98 if (sccMode & 0x20) {
99 // SCC+ range: 0xB800..0xBFFF, excluding 0xBFFE-0xBFFF
100 return (bankRegs[3] & 0x80) && (0xB800 <= addr) && (addr < 0xBFFE);
101 } else {
102 // SCC range: 0x9800..0x9FFF, excluding 0x9FFE-0x9FFF
103 return ((bankRegs[2] & 0x3F) == 0x3F) && (0x9800 <= addr) && (addr < 0x9FFE);
104 }
105}
106
107byte ReproCartridgeV1::readMem(word addr, EmuTime::param time)
108{
109 if (isSCCAccess(addr)) {
110 return scc.readMem(narrow_cast<uint8_t>(addr & 0xFF), time);
111 }
112
113 unsigned flashAddr = getFlashAddr(addr);
114 return (flashAddr != unsigned(-1))
115 ? flash.read(flashAddr)
116 : 0xFF; // unmapped read
117}
118
119byte ReproCartridgeV1::peekMem(word addr, EmuTime::param time) const
120{
121 if (isSCCAccess(addr)) {
122 return scc.peekMem(narrow_cast<uint8_t>(addr & 0xFF), time);
123 }
124
125 unsigned flashAddr = getFlashAddr(addr);
126 return (flashAddr != unsigned(-1))
127 ? flash.peek(flashAddr)
128 : 0xFF; // unmapped read
129}
130
132{
133 if (isSCCAccess(addr)) return nullptr;
134
135 unsigned flashAddr = getFlashAddr(addr);
136 return (flashAddr != unsigned(-1))
137 ? flash.getReadCacheLine(flashAddr)
138 : unmappedRead.data();
139}
140
141void ReproCartridgeV1::writeMem(word addr, byte value, EmuTime::param time)
142{
143 unsigned page8kB = (addr >> 13) - 2;
144 if (page8kB >= 4) return; // outside [0x4000, 0xBFFF]
145
146 // There are several overlapping functional regions in the address
147 // space. A single write can trigger behaviour in multiple regions. In
148 // other words there's no priority amongst the regions where a higher
149 // priority region blocks the write from the lower priority regions.
150 // This only goes for places where the flash is 'seen', so not for the
151 // SCC registers
152
153 if (isSCCAccess(addr)) {
154 scc.writeMem(narrow_cast<uint8_t>(addr & 0xFF), value, time);
155 return; // write to SCC blocks write to other functions
156 }
157
158 // address is calculated before writes to other regions take effect
159 unsigned flashAddr = getFlashAddr(addr);
160
161 // Main mapper register
162 if (addr == 0x7FFF) {
163 flashRomWriteEnabled = (value == 0x50);
164 invalidateDeviceRCache(); // flush all to be sure
165 }
166
167 if (!flashRomWriteEnabled) {
168 // Konami-SCC
169 if ((addr & 0x1800) == 0x1000) {
170 // [0x5000,0x57FF] [0x7000,0x77FF]
171 // [0x9000,0x97FF] [0xB000,0xB7FF]
172 bankRegs[page8kB] = value;
173 invalidateDeviceRCache(0x4000 + 0x2000 * page8kB, 0x2000);
174 }
175
176 // SCC mode register
177 if ((addr & 0xFFFE) == 0xBFFE) {
178 sccMode = value;
179 scc.setMode((value & 0x20) ? SCC::Mode::Plus
181 invalidateDeviceRCache(0x9800, 0x800);
182 invalidateDeviceRCache(0xB800, 0x800);
183 }
184 } else {
185 if (flashAddr != unsigned(-1)) {
186 flash.write(flashAddr, value);
187 }
188 }
189}
190
192{
193 return ((0x4000 <= addr) && (addr < 0xC000))
194 ? nullptr // [0x4000,0xBFFF] isn't cacheable
195 : unmappedWrite.data();
196}
197
198void ReproCartridgeV1::writeIO(word port, byte value, EmuTime::param time)
199{
200 switch (port & 0xFF)
201 {
202 case 0x10:
203 psgLatch = value & 0x0F;
204 break;
205 case 0x11:
206 psg.writeRegister(psgLatch, value, time);
207 break;
208 case 0x13:
209 mainBankReg = value & 3;
210 invalidateDeviceRCache(); // flush all to be sure
211 break;
212 default: UNREACHABLE;
213 }
214}
215
216template<typename Archive>
217void ReproCartridgeV1::serialize(Archive& ar, unsigned /*version*/)
218{
219 // skip MSXRom base class
220 ar.template serializeBase<MSXDevice>(*this);
221
222 ar.serialize("flash", flash,
223 "scc", scc,
224 "psg", psg,
225 "psgLatch", psgLatch,
226 "flashRomWriteEnabled", flashRomWriteEnabled,
227 "mainBankReg", mainBankReg,
228 "sccMode", sccMode,
229 "bankRegs", bankRegs);
230}
233
234} // namespace openmsx
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition MSXDevice.hh:356
void reset(EmuTime::param time)
Definition AY8910.cc:521
void writeRegister(unsigned reg, uint8_t value, EmuTime::param time)
Definition AY8910.cc:578
void write(size_t address, uint8_t value)
Definition AmdFlash.cc:466
const uint8_t * getReadCacheLine(size_t address) const
Definition AmdFlash.cc:455
uint8_t read(size_t address)
Definition AmdFlash.cc:444
uint8_t peek(size_t address) const
Definition AmdFlash.cc:206
static DummyAY8910Periphery & instance()
void invalidateDeviceRCache()
Definition MSXDevice.hh:215
static std::array< byte, 0x10000 > unmappedRead
Definition MSXDevice.hh:306
static std::array< byte, 0x10000 > unmappedWrite
Definition MSXDevice.hh:307
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.
byte * getWriteCacheLine(word address) override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
ReproCartridgeV1(const DeviceConfig &config, Rom &&rom)
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 setMode(Mode newMode)
Definition SCC.cc:183
void powerUp(EmuTime::param time)
Definition SCC.cc:141
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
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
constexpr void iota(ForwardIt first, ForwardIt last, T value)
Definition ranges.hh:314
STL namespace.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define UNREACHABLE