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
51 getCPUInterface().register_IO_Out(0x10, this);
52 getCPUInterface().register_IO_Out(0x11, this);
53 getCPUInterface().register_IO_Out(0x13, this);
54}
55
62
63void ReproCartridgeV1::powerUp(EmuTime::param time)
64{
65 scc.powerUp(time);
66 reset(time);
67}
68
69void ReproCartridgeV1::reset(EmuTime::param time)
70{
71 flashRomWriteEnabled = false;
72 mainBankReg = 0;
73 sccMode = 0;
74 ranges::iota(bankRegs, byte(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
85unsigned 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
95bool 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
116byte ReproCartridgeV1::readMem(word addr, EmuTime::param time)
117{
118 if (isSCCAccess(addr)) {
119 return scc.readMem(narrow_cast<uint8_t>(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
128byte ReproCartridgeV1::peekMem(word addr, EmuTime::param time) const
129{
130 if (isSCCAccess(addr)) {
131 return scc.peekMem(narrow_cast<uint8_t>(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.data();
148}
149
150void 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(narrow_cast<uint8_t>(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.data();
205}
206
207void 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
225template<typename Archive>
226void 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
#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 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: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:9
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