openMSX
ReproCartridgeV2.cc
Go to the documentation of this file.
1#include "ReproCartridgeV2.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 2 is similar to Konami Ultimate Collection. It
14 uses the same flashROM, SCC/SCC+. But 2 PSG's and volume control and
15 multiple mappers.
16
17 Released were cartridge with the following content: Only Metal Gear, only
18 Metal Gear 2, Vampire Killer (original, Castlevania patch, enhanced SCC),
19 Usas (original, enhanced), Nemesis 3 (enhanced) and Metal Gear Collection
20 (Metal Gear 1 and 2, both in 3 languages)
21
22
23[REGISTER (#7FFE)]
24Selects the mapper, using bits 0 and 1 (other bits are ignored):
25 00 = Konami SCC
26 01 = Konami
27 10 = ASCII8
28 11 = ASCII16
29
30[REGISTER (#7FFF)]
31If it contains value 0x50, the flash is writable and the mapper is disabled.
32Otherwise, the mapper is enabled and the flash is readonly.
33
34- Mapper supports 8 different ROMs of 1MB each, with the above mappers.
35- Cartridge has a PSG at 0x10 and a PSG at 0xA0, both write only
36- On I/O port 0x33 the 1MB block can be selected (default 0, so up to 7)
37- On I/O port 0x31 the volume can be selected of the 3 sound chips:
38 Bits EEIIISSS: EE(7-6) = PSG#10, III(5-3) = PSG#A0, SSS(2-0) = SCC
39 Default value is &B10000010. 0 means silent. So, clone PSG is silent by
40 default.
41
42Note: there is also a version 1 of this hardware, with the following
43differences:
44- Only Konami SCC mapper (no register at #7FFE)
45- No volume control register behind port #31
46- Main bank register is behind port #13 instead of #33
47- Main block size is 2MB instead of 1MB
48- No extra PSG at 0xA0 (but the PSG at #10 is there)
49
50******************************************************************************/
51
52namespace openmsx {
53
54static constexpr auto sectorInfo = [] {
55 // 8 * 8kB, followed by 127 * 64kB
56 using Info = AmdFlash::SectorInfo;
57 std::array<Info, 8 + 127> result = {};
58 std::fill(result.begin(), result.begin() + 8, Info{ 8 * 1024, false});
59 std::fill(result.begin() + 8, result.end(), Info{64 * 1024, false});
60 return result;
61}();
62
63
65 const DeviceConfig& config, Rom&& rom_)
66 : MSXRom(config, std::move(rom_))
67 , flash(rom, sectorInfo, 0x207E,
68 AmdFlash::Addressing::BITS_12, config)
69 , scc("ReproCartV2 SCC", config, getCurrentTime(), SCC::SCC_Compatible)
70 , psg0x10("ReproCartV2 PSG@0x10", DummyAY8910Periphery::instance(), config,
71 getCurrentTime())
72 , psg0xA0("ReproCartV2 PSG@0xA0", DummyAY8910Periphery::instance(), config,
73 getCurrentTime())
74{
76
77 getCPUInterface().register_IO_Out(0x10, this);
78 getCPUInterface().register_IO_Out(0x11, this);
79 getCPUInterface().register_IO_Out(0x31, this);
80 getCPUInterface().register_IO_Out(0x33, this);
81 getCPUInterface().register_IO_Out(0xA0, this);
82 getCPUInterface().register_IO_Out(0xA1, this);
83}
84
86{
93}
94
95void ReproCartridgeV2::powerUp(EmuTime::param time)
96{
97 scc.powerUp(time);
98 reset(time);
99}
100
101void ReproCartridgeV2::reset(EmuTime::param time)
102{
103 flashRomWriteEnabled = false;
104 mainBankReg = 0;
105 setVolume(time, 0b10'000'10);
106 mapperTypeReg = 0;
107 sccMode = 0;
108 ranges::iota(bankRegs, 0);
109
110 scc.reset(time);
111 psg0x10Latch = 0;
112 psg0x10.reset(time);
113 psg0xA0Latch = 0;
114 psg0xA0.reset(time);
115
116 flash.reset();
117
118 invalidateDeviceRCache(); // flush all to be sure
119}
120
121unsigned ReproCartridgeV2::getFlashAddr(unsigned addr) const
122{
123 unsigned page8kB = (addr >> 13) - 2;
124 if (page8kB >= 4) return unsigned(-1); // outside [0x4000, 0xBFFF]
125
126 byte bank = bankRegs[page8kB] & 127; // 1MB max
127 return (mainBankReg << 20) | (bank << 13) | (addr & 0x1FFF);
128}
129
130// Note: implementation (mostly) copied from KUC
131bool ReproCartridgeV2::isSCCAccess(word addr) const
132{
133 if ((mapperTypeReg != 0) || (sccMode & 0x10)) return false;
134
135 if (addr & 0x0100) {
136 // Address bit 8 must be zero, this is different from a real
137 // SCC/SCC+. According to Manuel Pazos this is a leftover from
138 // an earlier version that had 2 SCCs: the SCC on the left or
139 // right channel reacts when address bit 8 is respectively 0/1.
140 return false;
141 }
142
143 if (sccMode & 0x20) {
144 // SCC+ range: 0xB800..0xBFFF, excluding 0xBFFE-0xBFFF
145 return (bankRegs[3] & 0x80) && (0xB800 <= addr) && (addr < 0xBFFE);
146 } else {
147 // SCC range: 0x9800..0x9FFF, excluding 0x9FFE-0x9FFF
148 return ((bankRegs[2] & 0x3F) == 0x3F) && (0x9800 <= addr) && (addr < 0x9FFE);
149 }
150}
151
152byte ReproCartridgeV2::readMem(word addr, EmuTime::param time)
153{
154 if (isSCCAccess(addr)) {
155 return scc.readMem(addr & 0xFF, time);
156 }
157
158 unsigned flashAddr = getFlashAddr(addr);
159 return (flashAddr != unsigned(-1))
160 ? flash.read(flashAddr)
161 : 0xFF; // unmapped read
162}
163
164byte ReproCartridgeV2::peekMem(word addr, EmuTime::param time) const
165{
166 if (isSCCAccess(addr)) {
167 return scc.peekMem(addr & 0xFF, time);
168 }
169
170 unsigned flashAddr = getFlashAddr(addr);
171 return (flashAddr != unsigned(-1))
172 ? flash.peek(flashAddr)
173 : 0xFF; // unmapped read
174}
175
177{
178 if (isSCCAccess(addr)) return nullptr;
179
180 unsigned flashAddr = getFlashAddr(addr);
181 return (flashAddr != unsigned(-1))
182 ? flash.getReadCacheLine(flashAddr)
183 : unmappedRead.data();
184}
185
186void ReproCartridgeV2::writeMem(word addr, byte value, EmuTime::param time)
187{
188 unsigned page8kB = (addr >> 13) - 2;
189 if (page8kB >= 4) return; // outside [0x4000, 0xBFFF]
190
191 // There are several overlapping functional regions in the address
192 // space. A single write can trigger behaviour in multiple regions. In
193 // other words there's no priority amongst the regions where a higher
194 // priority region blocks the write from the lower priority regions.
195 // This only goes for places where the flash is 'seen', so not for the
196 // SCC registers
197
198 if (isSCCAccess(addr)) {
199 scc.writeMem(addr & 0xFF, value, time);
200 return; // write to SCC blocks write to other functions
201 }
202
203 // address is calculated before writes to other regions take effect
204 unsigned flashAddr = getFlashAddr(addr);
205
206 // Main mapper register
207 if (addr == 0x7FFF) {
208 flashRomWriteEnabled = (value == 0x50);
209 invalidateDeviceRCache(); // flush all to be sure
210 }
211 // Mapper selection register
212 if (addr == 0x7FFE) {
213 mapperTypeReg = value & 3; // other bits are ignored, so no need to store
214 invalidateDeviceRCache(); // flush all to be sure
215 }
216
217 if (!flashRomWriteEnabled) {
218 switch (mapperTypeReg) {
219 case 0:
220 // Konami-SCC
221 if ((addr & 0x1800) == 0x1000) {
222 // [0x5000,0x57FF] [0x7000,0x77FF]
223 // [0x9000,0x97FF] [0xB000,0xB7FF]
224 bankRegs[page8kB] = value;
225 invalidateDeviceRCache(0x4000 + 0x2000 * page8kB, 0x2000);
226 }
227
228 // SCC mode register
229 if ((addr & 0xFFFE) == 0xBFFE) {
230 sccMode = value;
231 scc.setChipMode((value & 0x20) ? SCC::SCC_plusmode
233 invalidateDeviceRCache(0x9800, 0x800);
234 invalidateDeviceRCache(0xB800, 0x800);
235 }
236 break;
237 case 1:
238 {
239 // Konami
240 // (Copied from MegaFlashROMSCCPlus)
241 // Masking of the mapper bits is done on
242 // write (and only in Konami(-scc) mode)
243 if ((addr < 0x5000) || ((0x5800 <= addr) && (addr < 0x6000))) break; // only SCC range works
244 bankRegs[page8kB] = value & 0x7F;
245 invalidateDeviceRCache(0x4000 + 0x2000 * page8kB, 0x2000);
246 break;
247 }
248 case 2:
249 // ASCII-8
250 // (Copied from MegaFlashROMSCCPlus)
251 if ((0x6000 <= addr) && (addr < 0x8000)) {
252 byte bank = (addr >> 11) & 0x03;
253 bankRegs[bank] = value;
254 invalidateDeviceRCache(0x4000 + 0x2000 * bank, 0x2000);
255 }
256 break;
257 case 3:
258 // ASCII-16
259 // (Copied from MegaFlashROMSCCPlus)
260 // This behaviour is confirmed by Manuel Pazos (creator
261 // of the cartridge): ASCII-16 uses all 4 bank registers
262 // and one bank switch changes 2 registers at once.
263 // This matters when switching mapper mode, because
264 // the content of the bank registers is unchanged after
265 // a switch.
266 if ((0x6000 <= addr) && (addr < 0x6800)) {
267 bankRegs[0] = 2 * value + 0;
268 bankRegs[1] = 2 * value + 1;
269 invalidateDeviceRCache(0x4000, 0x4000);
270 }
271 if ((0x7000 <= addr) && (addr < 0x7800)) {
272 bankRegs[2] = 2 * value + 0;
273 bankRegs[3] = 2 * value + 1;
274 invalidateDeviceRCache(0x8000, 0x4000);
275 }
276 break;
277 default:
279 }
280 } else {
281 if (flashAddr != unsigned(-1)) {
282 flash.write(flashAddr, value);
283 }
284 }
285}
286
288{
289 return ((0x4000 <= addr) && (addr < 0xC000))
290 ? nullptr // [0x4000,0xBFFF] isn't cacheable
291 : unmappedWrite.data();
292}
293
294void ReproCartridgeV2::writeIO(word port, byte value, EmuTime::param time)
295{
296 switch (port & 0xFF)
297 {
298 case 0x10:
299 psg0x10Latch = value & 0x0F;
300 break;
301 case 0x11:
302 psg0x10.writeRegister(psg0x10Latch, value, time);
303 break;
304 case 0xA0:
305 psg0xA0Latch = value & 0x0F;
306 break;
307 case 0xA1:
308 psg0xA0.writeRegister(psg0xA0Latch, value, time);
309 break;
310 case 0x31:
311 setVolume(time, value);
312 break;
313 case 0x33:
314 mainBankReg = value & 7;
315 invalidateDeviceRCache(); // flush all to be sure
316 break;
317 default: UNREACHABLE;
318 }
319}
320
321void ReproCartridgeV2::setVolume(EmuTime::param time, byte value)
322{
323 // store (mostly for the save/loadstate feature)
324 volumeReg = value;
325 // EE (7-6) = PSG#10. So values 0-3
326 // III(5-3) = PSG#A0. So values 0-7
327 // SSS(2-0) = SCC. So values 0-7
328 scc .setSoftwareVolume(narrow<float>((volumeReg >> 0) & 7) / 2.0f, time);
329 psg0xA0.setSoftwareVolume(narrow<float>((volumeReg >> 3) & 7) / 2.0f, time);
330 psg0x10.setSoftwareVolume(narrow<float>((volumeReg >> 6) & 3) / 2.0f, time);
331}
332
333template<typename Archive>
334void ReproCartridgeV2::serialize(Archive& ar, unsigned /*version*/)
335{
336 // skip MSXRom base class
337 ar.template serializeBase<MSXDevice>(*this);
338
339 ar.serialize("flash", flash,
340 "scc", scc,
341 "psg0x10", psg0x10,
342 "psg0x10Latch", psg0x10Latch,
343 "psg0xA0", psg0xA0,
344 "psg0xA0Latch", psg0xA0Latch,
345 "flashRomWriteEnabled", flashRomWriteEnabled,
346 "mainBankReg", mainBankReg,
347 "volumeReg", volumeReg,
348 "mapperTypeReg", mapperTypeReg,
349 "sccMode", sccMode,
350 "bankRegs", bankRegs);
351
352 if constexpr (Archive::IS_LOADER) {
353 auto time = getCurrentTime();
354 setVolume(time, volumeReg);
355 }
356
357}
360
361} // 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.
byte * getWriteCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
void reset(EmuTime::param time) override
This method is called on reset.
ReproCartridgeV2(const DeviceConfig &config, Rom &&rom)
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 writeMem(word address, byte value, EmuTime::param time) override
Write a given byte to a given location at a certain time to this device.
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
void setVolume(EmuTime::param time, byte value)
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
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
void setSoftwareVolume(float volume, EmuTime::param time)
Change the 'software volume' of this sound device.
Definition: SoundDevice.cc:143
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