openMSX
MegaFlashRomSCCPlusSD.cc
Go to the documentation of this file.
3#include "MSXCPUInterface.hh"
4#include "CacheLine.hh"
5#include "CheckedRam.hh"
6#include "SdCard.hh"
7#include "enumerate.hh"
8#include "narrow.hh"
9#include "ranges.hh"
10#include "serialize.hh"
11#include "xrange.hh"
12#include <array>
13#include <memory>
14
15/******************************************************************************
16 * DOCUMENTATION AS PROVIDED BY MANUEL PAZOS, WHO DEVELOPED THE CARTRIDGE *
17 ******************************************************************************
18
19--------------------------------------------------------------------------------
20MegaFlashROM SCC+ SD Technical Details
21(c) Manuel Pazos 24-02-2014
22--------------------------------------------------------------------------------
23
24[Main features]
25
26 - 8192 KB flashROM memory
27 - SD interface (MegaSD)
28 - SCC-I (2312P001)
29 - PSG (AY-3-8910/YM2149)
30 - Mappers: ASCII8, ASCII16, Konami, Konami SCC, linear 64K
31 - Slot expander
32
33
34--------------------------------------------------------------------------------
35[Memory]
36
37 - Model Numonyx / Micron M29W640FB / M29W640GB TSOP48
38 - Datasheet: https://media-www.micron.com/-/media/client/global/documents/products/data-sheet/nor-flash/parallel/m29w/m29w640f.pdf
39 https://media-www.micron.com/-/media/client/global/documents/products/data-sheet/nor-flash/parallel/m29w/m29w640g.pdf
40 - Block layout:
41 #00000 8K x 8
42 #10000 64K x 127
43 - Command addresses:
44 #4555 and #4AAA
45 - FlashROM ID:
46 ID_M29W640FB #FD
47 ID_M29W640GB #7E 10 00
48
49
50--------------------------------------------------------------------------------
51[PSG]
52
53 The PSG included in the cartridge is mapped to ports #10-#12
54
55 Port #A0 -> #10
56 Port #A1 -> #11
57 Port #A2 -> #12
58
59 The PSG is read only.
60
61--------------------------------------------------------------------------------
62[Cartridge layout]
63
64 - Subslot 0: Recovery - 16K linear (Visible on page 1 and 2)
65 - Subslot 1: MegaFlashROM SCC+ - 7104K (multiple mapper support)
66 - Subslot 2: RAM (when available)
67 - Subslot 3: MegaSD - 1024K (ASC8 mapper)
68
69
70--------------------------------------------------------------------------------
71[FlashROM layout]
72
73#000000+----------------+
74 | Recovery | 16K - Subslot 0 (Note: Blocks are write protected
75by VPP/WD pin)
76 +----------------+
77 | DSK Kernel | 16K
78 +----------------+
79 | Not used | 32K
80#010000+----------------+
81 | |
82 | |
83 | MegaFlashROM | 7104K - Subslot 1
84 | |
85 | |
86#700000+----------------+
87 | |
88 | MegaSD | 1024K - Subslot 3
89 | |
90 +----------------+
91
92
93--------------------------------------------------------------------------------
94[Subslot register (#FFFF)]
95
96 Available when writing at #FFFF in the cartridge slot.
97 Reading that address will return all the bits inverted.
98 Default value = 0
99
100
101--------------------------------------------------------------------------------
102[Subslot 0: RECOVERY]
103
104 Size 16K
105 Common ROM (Without mapper).
106 Visible on pages 1 and 2 (mirrored all over the slot)
107
108
109--------------------------------------------------------------------------------
110[Subslot 1: MegaFlashROM SCC+ SD]
111
112 [REGISTERS]
113
114 [MAPPER REGISTER (#7FFF)]
115 7 mapper mode 1: \ #00 = SCC, #40 = 64K
116 6 mapper mode 0: / #80 = ASC8, #C0 = ASC16
117 5 mapper mode : Select Konami mapper (0=SCC or 1=normal)
118 4
119 3 Disable #4000-#5FFF mapper in Konami mode
120 2 Disable this mapper register #7FFF
121 1 Disable mapper and offset registers
122 0 Enable 512K mapper limit in SCC mapper or 256K limit in Konami mapper
123
124
125 [OFFSET REGISTER (#7FFD)]
126 7-0 Offset value bits 7-0
127
128
129 [OFFSET REGISTER (#7FFE)]
130 1 Offset bit 9
131 0 Offset bit 8
132
133
134 [CONFIG REGISTER (#7FFC)]
135 7 Disable config register (1 = Disabled)
136 6
137 5 Disable SRAM (i.e. the RAM in subslot 2)
138 4 DSK mode (1 = On): Bank 0 and 1 are remapped to DSK kernel (config banks 2-3)
139 3 Cartridge PSG also mapped to ports #A0-#A3
140 2 Subslots disabled (1 = Disabled) Only MegaFlashROM SCC+ is available.
141 1 FlashROM Block protect (1 = Protect) VPP_WD pin
142 0 FlashROM write enable (1 = Enabled)
143
144
145
146 [MAPPERS]
147
148 - ASCII 8: Common ASC8 mapper
149
150 - ASCII 16: Common ASC16 mapper
151
152 - Konami: Common Konami mapper.
153 Bank0 (#4000-#5FFF) can be also changed unless [MAPPER REGISTER] bit 3 is 1
154
155 - Konami SCC: Common Konami SCC mapper
156
157 - Linear 64: #0000-#3FFF bank0
158 #4000-#7FFF bank1
159 #8000-#BFFF bank2
160 #C000-#FFFF bank3
161 Banks mapper registers addresses = Konami
162
163
164 [DEFAULT VALUES]
165
166 - MAPPER REGISTER = 0
167 - CONFIG REGISTER = %00000011
168 - MapperBank0 = 0
169 - MapperBank1 = 1
170 - MapperBank2 = 2
171 - MapperBank3 = 3
172 - BankOffset = 0
173 - Subslot register = 0
174
175
176 [LOGIC]
177
178 Bank0 <= "1111111010" when CONFIG REGISTER(4) = '1' and MapperBank0 = "0000000000" else ; DSK mode
179 MapperBank0 + bankOffset;
180
181 Bank1 <= "1111111011" when CONFIG REGISTER(4) = '1' and MapperBank1 = "0000000001" else ; DSK mode
182 MapperBank1 + bankOffset;
183
184 Bank2 <= MapperBank2 + bankOffset;
185
186 Bank3 <= MapperBank3 + bankOffset;
187
188
189 RamAdr <=
190 -- Mapper in 64K mode
191 Bank0(8 downto 0) & adr(13 downto 0) when adr(15 downto 14) = "00" else --#0000-#3FFF
192 Bank1(8 downto 0) & adr(13 downto 0) when adr(15 downto 14) = "01" else --#4000-#7FFF
193 Bank2(8 downto 0) & adr(13 downto 0) when adr(15 downto 14) = "10" else --#8000-#BFFF
194 Bank3(8 downto 0) & adr(13 downto 0) when adr(15 downto 14) = "11" else --#C000-#FFFF
195
196 -- Mapper in SCC, ASC8 or ASC16 modes
197 Bank0(9 downto 0) & adr(12 downto 0) when adr(14 downto 13) = "10" else --#4000-#5FFF
198 Bank1(9 downto 0) & adr(12 downto 0) when adr(14 downto 13) = "11" else --#6000-#7FFF
199 Bank2(9 downto 0) & adr(12 downto 0) when adr(14 downto 13) = "00" else --#8000-#9FFF
200 Bank3(9 downto 0) & adr(12 downto 0); --#A000-#BFFF
201
202
203 Note: It is possible to access the whole flashROM from the MegaFlashROM SCC+
204 SD using the offsets register!
205
206--------------------------------------------------------------------------------
207[Subslot 2: 512K RAM expansion]
208
209 Mapper ports:
210 Page 0 = #FC
211 Page 1 = #FD
212 Page 2 = #FE
213 Page 3 = #FF
214
215 Default bank values:
216 Page 0 = 3
217 Page 1 = 2
218 Page 2 = 1
219 Page 3 = 0
220
221 Disabled when [CONFIG REGISTER] bit 5 = 1
222
223 Since mapper ports must not be read, as stated on MSX Technical Handbook, and
224 mapper ports as read only, as stated on MSX Datapack, all read operations on
225 these ports will not return any value.
226
227UPDATE:
228 Initial version indeed did not support reading the memory mapper ports. But
229 upon user request this feature was added later.
230
231--------------------------------------------------------------------------------
232[Subslot 3: MegaSD]
233
234 Mapper type: ASCII8
235
236 Default mapper values:
237 Bank0 = 0
238 Bank1 = 1
239 Bank2 = 0
240 Bank3 = 0
241
242 Memory range 1024K: Banks #00-#7F are mirrored in #80-#FF (except registers
243 bank #40-#7F)
244
245 Memory registers area (Bank #40-#7F):
246 #4000-#57FF: SD card access (R/W)
247 #4000-#4FFF: /CS signal = 0 - SD enabled
248 #5000-#5FFF: /CS signal = 1 - SD disabled
249
250 #5800-#5FFF: SD slot select (bit 0: 0 = SD slot 1, 1 = SD slot 2)
251
252 Writes to these registers do not write through to the FlashROM.
253
254 Cards work in SPI mode.
255 Signals used: CS, DI, DO, SCLK
256 When reading, 8 bits are read from DO
257 When writing, 8 bits are written to DI
258
259 SD specifications: https://www.sdcard.org/downloads/pls/simplified_specs/part1_410.pdf
260
261******************************************************************************/
262
263static constexpr unsigned MEMORY_MAPPER_SIZE = 512;
264static constexpr uint8_t MEMORY_MAPPER_MASK = (MEMORY_MAPPER_SIZE / 16) - 1;
265
266namespace openmsx {
267
269 : MSXDevice(config)
270 , flash("MFR SCC+ SD flash", AmdFlashChip::M29W640GB, {}, config)
271 , scc("MFR SCC+ SD SCC-I", config, getCurrentTime(), SCC::Mode::Compatible)
272 , psg("MFR SCC+ SD PSG", DummyAY8910Periphery::instance(), config,
273 getCurrentTime())
274 , checkedRam(config.getChildDataAsBool("hasmemorymapper", true) ?
275 std::make_unique<CheckedRam>(config, getName() + " memory mapper", "memory mapper", MEMORY_MAPPER_SIZE * 1024)
276 : nullptr)
277 , mapperIO(checkedRam ? std::make_unique<MapperIO>(*this) : nullptr) // handles ports 0xfc-0xff
278{
280 getCPUInterface().register_IO_Out_range(0x10, 2, this);
281
282 sdCard[0] = std::make_unique<SdCard>(DeviceConfig(config, config.findChild("sdcard1")));
283 sdCard[1] = std::make_unique<SdCard>(DeviceConfig(config, config.findChild("sdcard2")));
284}
285
287{
288 // unregister extra PSG I/O ports
289 updateConfigReg(3);
291}
292
293void MegaFlashRomSCCPlusSD::powerUp(EmuTime::param time)
294{
295 scc.powerUp(time);
296 reset(time);
297}
298
299void MegaFlashRomSCCPlusSD::reset(EmuTime::param time)
300{
301 mapperReg = 0;
302 offsetReg = 0;
303 updateConfigReg(3);
304 subslotReg = 0;
305 ranges::iota(bankRegsSubSlot1, uint16_t(0));
306
307 sccMode = 0;
308 ranges::iota(sccBanks, byte(0));
309 scc.reset(time);
310
311 psgLatch = 0;
312 psg.reset(time);
313
314 flash.reset();
315
316 // memory mapper
317 for (auto [i, mr] : enumerate(memMapperRegs)) {
318 mr = byte(3 - i);
319 }
320
321 for (auto [bank, reg] : enumerate(bankRegsSubSlot3)) {
322 reg = (bank == 1) ? 1 : 0;
323 }
324
325 selectedCard = 0;
326
327 invalidateDeviceRWCache(); // flush all to be sure
328}
329
330byte MegaFlashRomSCCPlusSD::getSubSlot(unsigned addr) const
331{
332 return isSlotExpanderEnabled() ?
333 (subslotReg >> (2 * (addr >> 14))) & 3 : 1;
334}
335
336void MegaFlashRomSCCPlusSD::writeToFlash(unsigned addr, byte value)
337{
338 if (isFlashRomWriteEnabled()) {
339 flash.write(addr, value);
340 } else {
341 // flash is write protected, this is implemented by not passing
342 // writes to flash at all.
343 }
344}
345
346byte MegaFlashRomSCCPlusSD::peekMem(word addr, EmuTime::param time) const
347{
348 if (isSlotExpanderEnabled() && (addr == 0xFFFF)) {
349 // read subslot register
350 return subslotReg ^ 0xFF;
351 }
352
353 switch (getSubSlot(addr)) {
354 case 0: return peekMemSubSlot0(addr);
355 case 1: return peekMemSubSlot1(addr, time);
356 case 2: return isMemoryMapperEnabled() ?
357 peekMemSubSlot2(addr) : 0xFF;
358 case 3: return peekMemSubSlot3(addr, time);
359 default: UNREACHABLE;
360 }
361}
362
363byte MegaFlashRomSCCPlusSD::readMem(word addr, EmuTime::param time)
364{
365 if (isSlotExpanderEnabled() && (addr == 0xFFFF)) {
366 // read subslot register
367 return subslotReg ^ 0xFF;
368 }
369
370 switch (getSubSlot(addr)) {
371 case 0: return readMemSubSlot0(addr);
372 case 1: return readMemSubSlot1(addr, time);
373 case 2: return isMemoryMapperEnabled() ?
374 readMemSubSlot2(addr) : 0xFF;
375 case 3: return readMemSubSlot3(addr, time);
376 default: UNREACHABLE;
377 }
378}
379
381{
382 if (isSlotExpanderEnabled() &&
383 ((addr & CacheLine::HIGH) == (0xFFFF & CacheLine::HIGH))) {
384 // read subslot register
385 return nullptr;
386 }
387
388 switch (getSubSlot(addr)) {
389 case 0: return getReadCacheLineSubSlot0(addr);
390 case 1: return getReadCacheLineSubSlot1(addr);
391 case 2: return isMemoryMapperEnabled() ?
392 getReadCacheLineSubSlot2(addr) : unmappedRead.data();
393 case 3: return getReadCacheLineSubSlot3(addr);
394 default: UNREACHABLE;
395 }
396}
397
398void MegaFlashRomSCCPlusSD::writeMem(word addr, byte value, EmuTime::param time)
399{
400 if (isSlotExpanderEnabled() && (addr == 0xFFFF)) {
401 // write subslot register
402 byte diff = value ^ subslotReg;
403 subslotReg = value;
404 for (auto i : xrange(4)) {
405 if (diff & (3 << (2 * i))) {
406 invalidateDeviceRWCache(0x4000 * i, 0x4000);
407 }
408 }
409 }
410
411 switch (getSubSlot(addr)) {
412 case 0: writeMemSubSlot0(addr, value); break;
413 case 1: writeMemSubSlot1(addr, value, time); break;
414 case 2: if (isMemoryMapperEnabled()) {
415 writeMemSubSlot2(addr, value);
416 }
417 break;
418 case 3: writeMemSubSlot3(addr, value, time); break;
419 default: UNREACHABLE;
420 }
421}
422
424{
425 if (isSlotExpanderEnabled() &&
426 ((addr & CacheLine::HIGH) == (0xFFFF & CacheLine::HIGH))) {
427 // read subslot register
428 return nullptr;
429 }
430
431 switch (getSubSlot(addr)) {
432 case 0: return getWriteCacheLineSubSlot0(addr);
433 case 1: return getWriteCacheLineSubSlot1(addr);
434 case 2: return isMemoryMapperEnabled() ?
435 getWriteCacheLineSubSlot2(addr) : unmappedWrite.data();
436 case 3: return getWriteCacheLineSubSlot3(addr);
437 default: UNREACHABLE;
438 }
439}
440
442
443byte MegaFlashRomSCCPlusSD::readMemSubSlot0(word addr)
444{
445 // read from the first 16kB of flash
446 // Pazos: ROM and flash can be accessed in all pages (0,1,2,3) (#0000-#FFFF)
447 return flash.read(addr & 0x3FFF);
448}
449
450byte MegaFlashRomSCCPlusSD::peekMemSubSlot0(word addr) const
451{
452 // read from the first 16kB of flash
453 // Pazos: ROM and flash can be accessed in all pages (0,1,2,3) (#0000-#FFFF)
454 return flash.peek(addr & 0x3FFF);
455}
456
457const byte* MegaFlashRomSCCPlusSD::getReadCacheLineSubSlot0(word addr) const
458{
459 return flash.getReadCacheLine(addr & 0x3FFF);
460}
461
462void MegaFlashRomSCCPlusSD::writeMemSubSlot0(word addr, byte value)
463{
464 // Pazos: ROM and flash can be accessed in all pages (0,1,2,3) (#0000-#FFFF)
465 writeToFlash(addr & 0x3FFF, value);
466}
467
468byte* MegaFlashRomSCCPlusSD::getWriteCacheLineSubSlot0(word /*addr*/)
469{
470 return nullptr; // flash isn't cacheable
471}
472
474
475void MegaFlashRomSCCPlusSD::updateConfigReg(byte value)
476{
477 if ((value ^ configReg) & 0x08) {
478 if (value & 0x08) {
479 for (auto port : {0xa0, 0xa1}) {
480 getCPUInterface().register_IO_Out(narrow_cast<byte>(port), this);
481 }
482 } else {
483 for (auto port : {0xa0, 0xa1}) {
484 getCPUInterface().unregister_IO_Out(narrow_cast<byte>(port), this);
485 }
486 }
487 }
488 configReg = value;
489 flash.setVppWpPinLow(isFlashRomBlockProtectEnabled());
490 invalidateDeviceRWCache(); // flush all to be sure
491}
492
493MegaFlashRomSCCPlusSD::SCCEnable MegaFlashRomSCCPlusSD::getSCCEnable() const
494{
495 if ((sccMode & 0x20) && (sccBanks[3] & 0x80)) {
496 return EN_SCCPLUS;
497 } else if ((!(sccMode & 0x20)) && ((sccBanks[2] & 0x3F) == 0x3F)) {
498 return EN_SCC;
499 } else {
500 return EN_NONE;
501 }
502}
503
504unsigned MegaFlashRomSCCPlusSD::getFlashAddrSubSlot1(unsigned addr) const
505{
506 unsigned page = is64KmapperConfigured() ? (addr >> 14) : ((addr >> 13) - 2);
507 unsigned size = is64KmapperConfigured() ? 0x4000 : 0x2000;
508
509 if (page >= 4) return unsigned(-1); // outside [0x4000, 0xBFFF] for non-64K mapper
510
511 unsigned bank = bankRegsSubSlot1[page];
512 if (isDSKmodeEnabled() && (page == 0) && (bank == 0)) {
513 bank = 0x3FA;
514 } else if (isDSKmodeEnabled() && (page == 1) && (bank == 1)) {
515 bank = 0x3FB;
516 } else { // not DSK mode
517 bank += offsetReg;
518 }
519
520 unsigned tmp = (bank * size) + (addr & (size - 1));
521 return (tmp + 0x010000) & 0x7FFFFF; // wrap at 8MB
522}
523
524byte MegaFlashRomSCCPlusSD::readMemSubSlot1(word addr, EmuTime::param time)
525{
526 if (isKonamiSCCmapperConfigured()) { // Konami SCC
527 SCCEnable enable = getSCCEnable();
528 if (((enable == EN_SCC) && (0x9800 <= addr) && (addr < 0xA000)) ||
529 ((enable == EN_SCCPLUS) && (0xB800 <= addr) && (addr < 0xC000))) {
530 byte val = scc.readMem(narrow_cast<uint8_t>(addr & 0xFF), time);
531 return val;
532 }
533 }
534
535 unsigned flashAddr = getFlashAddrSubSlot1(addr);
536 return (flashAddr != unsigned(-1))
537 ? flash.read(flashAddr)
538 : 0xFF; // unmapped read
539}
540
541byte MegaFlashRomSCCPlusSD::peekMemSubSlot1(word addr, EmuTime::param time) const
542{
543 if (isKonamiSCCmapperConfigured()) { // Konami SCC
544 SCCEnable enable = getSCCEnable();
545 if (((enable == EN_SCC) && (0x9800 <= addr) && (addr < 0xA000)) ||
546 ((enable == EN_SCCPLUS) && (0xB800 <= addr) && (addr < 0xC000))) {
547 byte val = scc.peekMem(narrow_cast<uint8_t>(addr & 0xFF), time);
548 return val;
549 }
550 }
551
552 unsigned flashAddr = getFlashAddrSubSlot1(addr);
553 return (flashAddr != unsigned(-1))
554 ? flash.peek(flashAddr)
555 : 0xFF; // unmapped read
556}
557
558const byte* MegaFlashRomSCCPlusSD::getReadCacheLineSubSlot1(word addr) const
559{
560 if (isKonamiSCCmapperConfigured()) {
561 SCCEnable enable = getSCCEnable();
562 if (((enable == EN_SCC) && (0x9800 <= addr) && (addr < 0xA000)) ||
563 ((enable == EN_SCCPLUS) && (0xB800 <= addr) && (addr < 0xC000))) {
564 return nullptr;
565 }
566 }
567
568 unsigned flashAddr = getFlashAddrSubSlot1(addr);
569 return (flashAddr != unsigned(-1))
570 ? flash.getReadCacheLine(flashAddr)
571 : unmappedRead.data();
572}
573
574void MegaFlashRomSCCPlusSD::writeMemSubSlot1(word addr, byte value, EmuTime::param time)
575{
576 // address is calculated before writes to other regions take effect
577 unsigned flashAddr = getFlashAddrSubSlot1(addr);
578
579 // There are several overlapping functional regions in the address
580 // space. A single write can trigger behaviour in multiple regions. In
581 // other words there's no priority amongst the regions where a higher
582 // priority region blocks the write from the lower priority regions.
583 // This only goes for places where the flash is 'seen', so not for the
584 // SCC registers and the SSR
585
586 if (!isConfigRegDisabled() && (addr == 0x7FFC)) {
587 // write config register
588 updateConfigReg(value);
589 }
590
591 if (!isMapperRegisterDisabled() && (addr == 0x7FFF)) {
592 // write mapper register
593 mapperReg = value;
594 invalidateDeviceRWCache(); // flush all to be sure
595 }
596
597 if (!areBankRegsAndOffsetRegsDisabled() && (addr == 0x7FFD)) {
598 // write offset register low part
599 offsetReg = (offsetReg & 0x300) | value;
601 }
602
603 if (!areBankRegsAndOffsetRegsDisabled() && (addr == 0x7FFE)) {
604 // write offset register high part (bit 8 and 9)
605 offsetReg = (offsetReg & 0xFF) + ((value & 0x3) << 8);
607 }
608
609 if (isKonamiSCCmapperConfigured()) {
610 // Konami-SCC
611 if ((addr & 0xFFFE) == 0xBFFE) {
612 sccMode = value;
613 scc.setMode((value & 0x20) ? SCC::Mode::Plus
614 : SCC::Mode::Compatible);
615 invalidateDeviceRWCache(0x9800, 0x800);
616 invalidateDeviceRWCache(0xB800, 0x800);
617 }
618 SCCEnable enable = getSCCEnable();
619 bool isRamSegment2 = ((sccMode & 0x24) == 0x24) ||
620 ((sccMode & 0x10) == 0x10);
621 bool isRamSegment3 = ((sccMode & 0x10) == 0x10);
622 if (((enable == EN_SCC) && !isRamSegment2 &&
623 (0x9800 <= addr) && (addr < 0xA000)) ||
624 ((enable == EN_SCCPLUS) && !isRamSegment3 &&
625 (0xB800 <= addr) && (addr < 0xC000))) {
626 scc.writeMem(narrow_cast<uint8_t>(addr & 0xFF), value, time);
627 return; // Pazos: when SCC registers are selected flashROM is not seen, so it does not accept commands.
628 }
629 }
630
631 unsigned page8kB = (addr >> 13) - 2;
632 if (!areBankRegsAndOffsetRegsDisabled() && (page8kB < 4)) {
633 // (possibly) write to bank registers
634 switch (mapperReg & 0xE0) {
635 case 0x00:
636 // Konami-SCC
637 if ((addr & 0x1800) == 0x1000) {
638 // Storing 'sccBanks' may seem redundant at
639 // first, but it's required to calculate
640 // whether the SCC is enabled or not.
641 sccBanks[page8kB] = value;
642 // Masking of the mapper bits is done on
643 // write (and only in Konami(-scc) mode)
644 byte mask = areKonamiMapperLimitsEnabled() ? 0x3F : 0xFF;
645 bankRegsSubSlot1[page8kB] = value & mask;
646 invalidateDeviceRWCache(0x4000 + 0x2000 * page8kB, 0x2000);
647 }
648 break;
649 case 0x20: {
650 // Konami
651 if (isWritingKonamiBankRegisterDisabled() && (addr < 0x6000)) {
652 // Switching 0x4000-0x5FFF disabled.
653 // This bit blocks writing to the bank register
654 // (an alternative was forcing a 0 on read).
655 // It only has effect in Konami mode.
656 break;
657 }
658 // Making of the mapper bits is done on
659 // write (and only in Konami(-scc) mode)
660 if ((addr < 0x5000) || ((0x5800 <= addr) && (addr < 0x6000))) break; // only SCC range works
661 byte mask = areKonamiMapperLimitsEnabled() ? 0x1F : 0xFF;
662 bankRegsSubSlot1[page8kB] = value & mask;
663 invalidateDeviceRWCache(0x4000 + 0x2000 * page8kB, 0x2000);
664 break;
665 }
666 case 0x40:
667 case 0x60:
668 // 64kB
669 bankRegsSubSlot1[page8kB] = value;
670 invalidateDeviceRWCache(0x0000 + 0x4000 * page8kB, 0x4000);
671 break;
672 case 0x80:
673 case 0xA0:
674 // ASCII-8
675 if ((0x6000 <= addr) && (addr < 0x8000)) {
676 byte bank = (addr >> 11) & 0x03;
677 bankRegsSubSlot1[bank] = value;
678 invalidateDeviceRWCache(0x4000 + 0x2000 * bank, 0x2000);
679 }
680 break;
681 case 0xC0:
682 case 0xE0:
683 // ASCII-16
684 // This behaviour is confirmed by Manuel Pazos (creator
685 // of the cartridge): ASCII-16 uses all 4 bank registers
686 // and one bank switch changes 2 registers at once.
687 // This matters when switching mapper mode, because
688 // the content of the bank registers is unchanged after
689 // a switch.
690 // In the first versions of the VHDL code, the bankRegs
691 // were 8-bit, but due to the construction explained
692 // above, this results in a maximum size of 2MB for the
693 // ASCII-16 mapper, as we throw away 1 bit. Later,
694 // Manuel Pazos made an update to use 9 bits for the
695 // registers to overcome this limitation. We emulate
696 // the updated version now.
697 const uint16_t mask = (1 << 9) - 1;
698 if ((0x6000 <= addr) && (addr < 0x6800)) {
699 bankRegsSubSlot1[0] = (2 * value + 0) & mask;
700 bankRegsSubSlot1[1] = (2 * value + 1) & mask;
701 invalidateDeviceRWCache(0x4000, 0x4000);
702 }
703 if ((0x7000 <= addr) && (addr < 0x7800)) {
704 bankRegsSubSlot1[2] = (2 * value + 0) & mask;
705 bankRegsSubSlot1[3] = (2 * value + 1) & mask;
706 invalidateDeviceRWCache(0x8000, 0x4000);
707 }
708 break;
709 }
710 }
711
712 if (flashAddr != unsigned(-1)) {
713 writeToFlash(flashAddr, value);
714 }
715}
716
717byte* MegaFlashRomSCCPlusSD::getWriteCacheLineSubSlot1(word /*addr*/)
718{
719 return nullptr; // flash isn't cacheable
720}
721
723
724unsigned MegaFlashRomSCCPlusSD::calcMemMapperAddress(word address) const
725{
726 auto bank = memMapperRegs[address >> 14];
727 return ((bank & MEMORY_MAPPER_MASK) << 14) | (address & 0x3FFF);
728}
729
730byte MegaFlashRomSCCPlusSD::readMemSubSlot2(word addr)
731{
732 // read from the memory mapper
733 return checkedRam->read(calcMemMapperAddress(addr));
734}
735
736byte MegaFlashRomSCCPlusSD::peekMemSubSlot2(word addr) const
737{
738 return checkedRam->peek(calcMemMapperAddress(addr));
739}
740
741const byte* MegaFlashRomSCCPlusSD::getReadCacheLineSubSlot2(word addr) const
742{
743 return checkedRam->getReadCacheLine(calcMemMapperAddress(addr));
744}
745
746void MegaFlashRomSCCPlusSD::writeMemSubSlot2(word addr, byte value)
747{
748 // write to the memory mapper
749 checkedRam->write(calcMemMapperAddress(addr), value);
750}
751
752byte* MegaFlashRomSCCPlusSD::getWriteCacheLineSubSlot2(word addr)
753{
754 return checkedRam->getWriteCacheLine(calcMemMapperAddress(addr));
755}
756
757byte MegaFlashRomSCCPlusSD::MapperIO::readIO(word port, EmuTime::param time)
758{
759 return peekIO(port, time);
760}
761
762byte MegaFlashRomSCCPlusSD::MapperIO::peekIO(word port, EmuTime::param /*time*/) const
763{
764 return getSelectedSegment(port & 3) | byte(~MEMORY_MAPPER_MASK);
765}
766
767void MegaFlashRomSCCPlusSD::MapperIO::writeIO(word port, byte value, EmuTime::param /*time*/)
768{
769 mega.memMapperRegs[port & 3] = value & MEMORY_MAPPER_MASK;
770 mega.invalidateDeviceRWCache(0x4000 * (port & 0x03), 0x4000);
771}
772
773byte MegaFlashRomSCCPlusSD::MapperIO::getSelectedSegment(byte page) const
774{
775 return mega.memMapperRegs[page];
776}
777
779
780unsigned MegaFlashRomSCCPlusSD::getFlashAddrSubSlot3(unsigned addr) const
781{
782 unsigned page8kB = (addr >> 13) - 2;
783 return (bankRegsSubSlot3[page8kB] & 0x7f) * 0x2000 + (addr & 0x1fff) + 0x700000;
784}
785
786byte MegaFlashRomSCCPlusSD::readMemSubSlot3(word addr, EmuTime::param /*time*/)
787{
788 if (((bankRegsSubSlot3[0] & 0xC0) == 0x40) && ((0x4000 <= addr) && (addr < 0x6000))) {
789 // transfer from SD card
790 return sdCard[selectedCard]->transfer(0xFF, (addr & 0x1000) != 0);
791 }
792
793 if ((0x4000 <= addr) && (addr < 0xC000)) {
794 // read (flash)rom content
795 unsigned flashAddr = getFlashAddrSubSlot3(addr);
796 return flash.read(flashAddr);
797 } else {
798 // unmapped read
799 return 0xFF;
800 }
801}
802
803byte MegaFlashRomSCCPlusSD::peekMemSubSlot3(word addr, EmuTime::param /*time*/) const
804{
805 if ((0x4000 <= addr) && (addr < 0xC000)) {
806 // read (flash)rom content
807 unsigned flashAddr = getFlashAddrSubSlot3(addr);
808 return flash.peek(flashAddr);
809 } else {
810 // unmapped read
811 return 0xFF;
812 }
813}
814
815const byte* MegaFlashRomSCCPlusSD::getReadCacheLineSubSlot3(word addr) const
816{
817 if (((bankRegsSubSlot3[0] & 0xC0) == 0x40) && ((0x4000 <= addr) && (addr < 0x6000))) {
818 return nullptr;
819 }
820
821 if ((0x4000 <= addr) && (addr < 0xC000)) {
822 // (flash)rom content
823 unsigned flashAddr = getFlashAddrSubSlot3(addr);
824 return flash.getReadCacheLine(flashAddr);
825 } else {
826 return unmappedRead.data();
827 }
828}
829
830void MegaFlashRomSCCPlusSD::writeMemSubSlot3(word addr, byte value, EmuTime::param /*time*/)
831{
832
833 if (((bankRegsSubSlot3[0] & 0xC0) == 0x40) && ((0x4000 <= addr) && (addr < 0x6000))) {
834 if (addr >= 0x5800) {
835 selectedCard = value & 1;
836 } else {
837 // transfer to SD card
838 sdCard[selectedCard]->transfer(value, (addr & 0x1000) != 0); // ignore return value
839 }
840 } else {
841 // write to flash (first, before modifying bank regs)
842 if ((0x4000 <= addr) && (addr < 0xC000)) {
843 unsigned flashAddr = getFlashAddrSubSlot3(addr);
844 writeToFlash(flashAddr, value);
845 }
846
847 // ASCII-8 mapper
848 if ((0x6000 <= addr) && (addr < 0x8000)) {
849 byte page8kB = (addr >> 11) & 0x03;
850 bankRegsSubSlot3[page8kB] = value;
851 invalidateDeviceRWCache(0x4000 + 0x2000 * page8kB, 0x2000);
852 }
853 }
854}
855
856byte* MegaFlashRomSCCPlusSD::getWriteCacheLineSubSlot3(word /*addr*/)
857{
858 return nullptr; // flash isn't cacheable
859}
860
862
863void MegaFlashRomSCCPlusSD::writeIO(word port, byte value, EmuTime::param time)
864{
865 switch (port & 0xFF) {
866 case 0xA0:
867 if (!isPSGalsoMappedToNormalPorts()) return;
868 [[fallthrough]];
869 case 0x10:
870 psgLatch = value & 0x0F;
871 break;
872
873 case 0xA1:
874 if (!isPSGalsoMappedToNormalPorts()) return;
875 [[fallthrough]];
876 case 0x11:
877 psg.writeRegister(psgLatch, value, time);
878 break;
879
880 default:
882 }
883}
884
885template<typename Archive>
886void MegaFlashRomSCCPlusSD::serialize(Archive& ar, unsigned /*version*/)
887{
888 // skip MSXRom base class
889 ar.template serializeBase<MSXDevice>(*this);
890
891 // overall
892 ar.serialize("flash", flash,
893 "subslotReg", subslotReg);
894
895 // subslot 0 stuff
896 // (nothing)
897
898 // subslot 1 stuff
899 ar.serialize("scc", scc,
900 "sccMode", sccMode,
901 "sccBanks", sccBanks,
902 "psg", psg,
903 "psgLatch", psgLatch,
904 "configReg", configReg,
905 "mapperReg", mapperReg,
906 "offsetReg", offsetReg,
907 "bankRegsSubSlot1", bankRegsSubSlot1);
908 if constexpr (Archive::IS_LOADER) {
909 // Re-register PSG ports (if needed)
910 byte tmp = configReg;
911 configReg = 3; // set to un-registered
912 updateConfigReg(tmp); // restore correct value
913 }
914
915 // subslot 2 stuff
916 // TODO ar.serialize("checkedRam", checkedRam);
917 if (checkedRam) ar.serialize("ram", checkedRam->getUncheckedRam());
918 ar.serialize("memMapperRegs", memMapperRegs);
919
920 // subslot 3 stuff
921 ar.serialize("bankRegsSubSlot3", bankRegsSubSlot3,
922 "selectedCard", selectedCard,
923 "sdCard0", *sdCard[0],
924 "sdCard1", *sdCard[1]);
925}
928
929} // 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:456
const uint8_t * getReadCacheLine(size_t address) const
Definition AmdFlash.cc:444
void setVppWpPinLow(bool value)
Setting the Vpp/WP# pin LOW enables a certain kind of write protection of some sectors.
Definition AmdFlash.hh:214
uint8_t read(size_t address)
Definition AmdFlash.cc:432
uint8_t peek(size_t address) const
Definition AmdFlash.cc:186
static DummyAY8910Periphery & instance()
void register_IO_Out(byte port, MSXDevice *device)
Devices can register their Out ports.
void unregister_IO_Out_range(byte port, unsigned num, MSXDevice *device)
void register_IO_Out_range(byte port, unsigned num, MSXDevice *device)
void unregister_IO_Out(byte port, MSXDevice *device)
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition MSXDevice.hh:36
static std::array< byte, 0x10000 > unmappedRead
Definition MSXDevice.hh:306
static std::array< byte, 0x10000 > unmappedWrite
Definition MSXDevice.hh:307
void invalidateDeviceRWCache()
Calls MSXCPUInterface::invalidateXXCache() for the specific (part of) the slot that this device is lo...
Definition MSXDevice.hh:214
EmuTime::param getCurrentTime() const
Definition MSXDevice.cc:125
MSXCPUInterface & getCPUInterface() const
Definition MSXDevice.cc:133
void serialize(Archive &ar, unsigned version)
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 reset(EmuTime::param time) override
This method is called on reset.
byte * getWriteCacheLine(word address) override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
MegaFlashRomSCCPlusSD(const DeviceConfig &config)
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 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 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
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
Definition enumerate.hh:28
constexpr unsigned HIGH
Definition CacheLine.hh:10
This file implemented 3 utility functions:
Definition Autofire.cc:11
uint8_t byte
8 bit unsigned integer
Definition openmsx.hh:26
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
constexpr void iota(ForwardIt first, ForwardIt last, T value)
Definition ranges.hh:322
STL namespace.
size_t size(std::string_view utf8)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define UNREACHABLE
constexpr auto xrange(T e)
Definition xrange.hh:132