openMSX
ReproCartridgeV1.cc
Go to the documentation of this file.
1#include "ReproCartridgeV1.hh"
3#include "MSXCPUInterface.hh"
4#include "serialize.hh"
5#include <array>
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)]
19If it contains value 0x50, the flash is writable and the mapper is disabled.
20Otherwise, 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
28namespace openmsx {
29
30static constexpr auto sectorInfo = [] {
31 // 8 * 8kB, followed by 127 * 64kB
32 using Info = AmdFlash::SectorInfo;
33 std::array<Info, 8 + 127> result = {};
34 std::fill(result.begin(), result.begin() + 8, Info{ 8 * 1024, false});
35 std::fill(result.begin() + 8, result.end(), Info{64 * 1024, false});
36 return result;
37}();
38
40 const DeviceConfig& config, Rom&& rom_)
41 : MSXRom(config, std::move(rom_))
42 , flash(rom, sectorInfo, 0x207E,
43 AmdFlash::Addressing::BITS_12, config)
44 , scc("ReproCartV1 SCC", config, getCurrentTime(), SCC::SCC_Compatible)
45 , psg("ReproCartV1 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{
60}
61
62void ReproCartridgeV1::powerUp(EmuTime::param time)
63{
64 scc.powerUp(time);
65 reset(time);
66}
67
68void ReproCartridgeV1::reset(EmuTime::param time)
69{
70 flashRomWriteEnabled = false;
71 mainBankReg = 0;
72 sccMode = 0;
73 ranges::iota(bankRegs, 0);
74
75 scc.reset(time);
76 psgLatch = 0;
77 psg.reset(time);
78
79 flash.reset();
80
81 invalidateDeviceRCache(); // flush all to be sure
82}
83
84unsigned ReproCartridgeV1::getFlashAddr(unsigned addr) const
85{
86 unsigned page8kB = (addr >> 13) - 2;
87 if (page8kB >= 4) return unsigned(-1); // outside [0x4000, 0xBFFF]
88
89 byte bank = bankRegs[page8kB]; // 2MB max
90 return (mainBankReg << 21) | (bank << 13) | (addr & 0x1FFF);
91}
92
93// Note: implementation (mostly) copied from KUC
94bool ReproCartridgeV1::isSCCAccess(word addr) const
95{
96 if (sccMode & 0x10) return false;
97
98 if (addr & 0x0100) {
99 // Address bit 8 must be zero, this is different from a real
100 // SCC/SCC+. According to Manuel Pazos this is a leftover from
101 // an earlier version that had 2 SCCs: the SCC on the left or
102 // right channel reacts when address bit 8 is respectively 0/1.
103 return false;
104 }
105
106 if (sccMode & 0x20) {
107 // SCC+ range: 0xB800..0xBFFF, excluding 0xBFFE-0xBFFF
108 return (bankRegs[3] & 0x80) && (0xB800 <= addr) && (addr < 0xBFFE);
109 } else {
110 // SCC range: 0x9800..0x9FFF, excluding 0x9FFE-0x9FFF
111 return ((bankRegs[2] & 0x3F) == 0x3F) && (0x9800 <= addr) && (addr < 0x9FFE);
112 }
113}
114
115byte ReproCartridgeV1::readMem(word addr, EmuTime::param time)
116{
117 if (isSCCAccess(addr)) {
118 return scc.readMem(addr & 0xFF, time);
119 }
120
121 unsigned flashAddr = getFlashAddr(addr);
122 return (flashAddr != unsigned(-1))
123 ? flash.read(flashAddr)
124 : 0xFF; // unmapped read
125}
126
127byte ReproCartridgeV1::peekMem(word addr, EmuTime::param time) const
128{
129 if (isSCCAccess(addr)) {
130 return scc.peekMem(addr & 0xFF, time);
131 }
132
133 unsigned flashAddr = getFlashAddr(addr);
134 return (flashAddr != unsigned(-1))
135 ? flash.peek(flashAddr)
136 : 0xFF; // unmapped read
137}
138
140{
141 if (isSCCAccess(addr)) return nullptr;
142
143 unsigned flashAddr = getFlashAddr(addr);
144 return (flashAddr != unsigned(-1))
145 ? flash.getReadCacheLine(flashAddr)
146 : unmappedRead.data();
147}
148
149void ReproCartridgeV1::writeMem(word addr, byte value, EmuTime::param time)
150{
151 unsigned page8kB = (addr >> 13) - 2;
152 if (page8kB >= 4) return; // outside [0x4000, 0xBFFF]
153
154 // There are several overlapping functional regions in the address
155 // space. A single write can trigger behaviour in multiple regions. In
156 // other words there's no priority amongst the regions where a higher
157 // priority region blocks the write from the lower priority regions.
158 // This only goes for places where the flash is 'seen', so not for the
159 // SCC registers
160
161 if (isSCCAccess(addr)) {
162 scc.writeMem(addr & 0xFF, value, time);
163 return; // write to SCC blocks write to other functions
164 }
165
166 // address is calculated before writes to other regions take effect
167 unsigned flashAddr = getFlashAddr(addr);
168
169 // Main mapper register
170 if (addr == 0x7FFF) {
171 flashRomWriteEnabled = (value == 0x50);
172 invalidateDeviceRCache(); // flush all to be sure
173 }
174
175 if (!flashRomWriteEnabled) {
176 // Konami-SCC
177 if ((addr & 0x1800) == 0x1000) {
178 // [0x5000,0x57FF] [0x7000,0x77FF]
179 // [0x9000,0x97FF] [0xB000,0xB7FF]
180 bankRegs[page8kB] = value;
181 invalidateDeviceRCache(0x4000 + 0x2000 * page8kB, 0x2000);
182 }
183
184 // SCC mode register
185 if ((addr & 0xFFFE) == 0xBFFE) {
186 sccMode = value;
187 scc.setChipMode((value & 0x20) ? SCC::SCC_plusmode
189 invalidateDeviceRCache(0x9800, 0x800);
190 invalidateDeviceRCache(0xB800, 0x800);
191 }
192 } else {
193 if (flashAddr != unsigned(-1)) {
194 flash.write(flashAddr, value);
195 }
196 }
197}
198
200{
201 return ((0x4000 <= addr) && (addr < 0xC000))
202 ? nullptr // [0x4000,0xBFFF] isn't cacheable
203 : unmappedWrite.data();
204}
205
206void ReproCartridgeV1::writeIO(word port, byte value, EmuTime::param time)
207{
208 switch (port & 0xFF)
209 {
210 case 0x10:
211 psgLatch = value & 0x0F;
212 break;
213 case 0x11:
214 psg.writeRegister(psgLatch, value, time);
215 break;
216 case 0x13:
217 mainBankReg = value & 3;
218 invalidateDeviceRCache(); // flush all to be sure
219 break;
220 default: UNREACHABLE;
221 }
222}
223
224template<typename Archive>
225void ReproCartridgeV1::serialize(Archive& ar, unsigned /*version*/)
226{
227 // skip MSXRom base class
228 ar.template serializeBase<MSXDevice>(*this);
229
230 ar.serialize("flash", flash,
231 "scc", scc,
232 "psg", psg,
233 "psgLatch", psgLatch,
234 "flashRomWriteEnabled", flashRomWriteEnabled,
235 "mainBankReg", mainBankReg,
236 "sccMode", sccMode,
237 "bankRegs", bankRegs);
238}
241
242} // namespace openmsx
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:211
static std::array< byte, 0x10000 > unmappedRead
Definition: MSXDevice.hh:302
static std::array< byte, 0x10000 > unmappedWrite
Definition: MSXDevice.hh:303
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
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
AmdFlash::SectorInfo Info
Definition: RomManbow2.cc:18
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
constexpr void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:287
constexpr void iota(ForwardIt first, ForwardIt last, T value)
Definition: ranges.hh:294
STL namespace.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1021
#define UNREACHABLE
Definition: unreachable.hh:38