260static constexpr unsigned MEMORY_MAPPER_SIZE = 512;
261static constexpr uint8_t MEMORY_MAPPER_MASK = (MEMORY_MAPPER_SIZE / 16) - 1;
265static constexpr auto sectorInfo = [] {
267 using Info = AmdFlash::SectorInfo;
268 std::array<Info, 8 + 127> result = {};
269 std::fill(result.begin(), result.begin() + 8,
Info{ 8 * 1024, false});
270 std::fill(result.begin() + 8, result.end(),
Info{64 * 1024, false});
277 , flash(
"MFR SCC+ SD flash", sectorInfo, 0x207E,
278 AmdFlash::Addressing::BITS_12, config)
279 , scc(
"MFR SCC+ SD SCC-I", config, getCurrentTime(),
SCC::SCC_Compatible)
282 , checkedRam(config.getChildDataAsBool(
"hasmemorymapper", true) ?
283 std::make_unique<
CheckedRam>(config,
getName() +
" memory mapper",
"memory mapper", MEMORY_MAPPER_SIZE * 1024)
285 , mapperIO(checkedRam ?
std::make_unique<MapperIO>(*this) : nullptr)
329 for (
auto [i, mr] :
enumerate(memMapperRegs)) {
333 for (
auto [bank, reg] :
enumerate(bankRegsSubSlot3)) {
334 reg = (bank == 1) ? 1 : 0;
342byte MegaFlashRomSCCPlusSD::getSubSlot(
unsigned addr)
const
344 return isSlotExpanderEnabled() ?
345 (subslotReg >> (2 * (addr >> 14))) & 3 : 1;
348void MegaFlashRomSCCPlusSD::writeToFlash(
unsigned addr,
byte value)
350 if (isFlashRomWriteEnabled()) {
351 flash.
write(addr, value);
360 if (isSlotExpanderEnabled() && (addr == 0xFFFF)) {
362 return subslotReg ^ 0xFF;
365 switch (getSubSlot(addr)) {
366 case 0:
return peekMemSubSlot0(addr);
367 case 1:
return peekMemSubSlot1(addr, time);
368 case 2:
return isMemoryMapperEnabled() ?
369 peekMemSubSlot2(addr) : 0xFF;
370 case 3:
return peekMemSubSlot3(addr, time);
377 if (isSlotExpanderEnabled() && (addr == 0xFFFF)) {
379 return subslotReg ^ 0xFF;
382 switch (getSubSlot(addr)) {
383 case 0:
return readMemSubSlot0(addr);
384 case 1:
return readMemSubSlot1(addr, time);
385 case 2:
return isMemoryMapperEnabled() ?
386 readMemSubSlot2(addr) : 0xFF;
387 case 3:
return readMemSubSlot3(addr, time);
394 if (isSlotExpanderEnabled() &&
400 switch (getSubSlot(addr)) {
401 case 0:
return getReadCacheLineSubSlot0(addr);
402 case 1:
return getReadCacheLineSubSlot1(addr);
403 case 2:
return isMemoryMapperEnabled() ?
405 case 3:
return getReadCacheLineSubSlot3(addr);
412 if (isSlotExpanderEnabled() && (addr == 0xFFFF)) {
414 byte diff = value ^ subslotReg;
416 for (
auto i :
xrange(4)) {
417 if (diff & (3 << (2 * i))) {
423 switch (getSubSlot(addr)) {
424 case 0: writeMemSubSlot0(addr, value);
break;
425 case 1: writeMemSubSlot1(addr, value, time);
break;
426 case 2:
if (isMemoryMapperEnabled()) {
427 writeMemSubSlot2(addr, value);
430 case 3: writeMemSubSlot3(addr, value, time);
break;
437 if (isSlotExpanderEnabled() &&
443 switch (getSubSlot(addr)) {
444 case 0:
return getWriteCacheLineSubSlot0(addr);
445 case 1:
return getWriteCacheLineSubSlot1(addr);
446 case 2:
return isMemoryMapperEnabled() ?
448 case 3:
return getWriteCacheLineSubSlot3(addr);
455byte MegaFlashRomSCCPlusSD::readMemSubSlot0(
word addr)
459 return flash.
read(addr & 0x3FFF);
462byte MegaFlashRomSCCPlusSD::peekMemSubSlot0(
word addr)
const
466 return flash.
peek(addr & 0x3FFF);
469const byte* MegaFlashRomSCCPlusSD::getReadCacheLineSubSlot0(
word addr)
const
474void MegaFlashRomSCCPlusSD::writeMemSubSlot0(
word addr,
byte value)
477 writeToFlash(addr & 0x3FFF, value);
480byte* MegaFlashRomSCCPlusSD::getWriteCacheLineSubSlot0(
word )
const
487void MegaFlashRomSCCPlusSD::updateConfigReg(
byte value)
489 if ((value ^ configReg) & 0x08) {
503MegaFlashRomSCCPlusSD::SCCEnable MegaFlashRomSCCPlusSD::getSCCEnable()
const
505 if ((sccMode & 0x20) && (sccBanks[3] & 0x80)) {
507 }
else if ((!(sccMode & 0x20)) && ((sccBanks[2] & 0x3F) == 0x3F)) {
514unsigned MegaFlashRomSCCPlusSD::getFlashAddrSubSlot1(
unsigned addr)
const
516 unsigned page = is64KmapperConfigured() ? (addr >> 14) : ((addr >> 13) - 2);
517 unsigned size = is64KmapperConfigured() ? 0x4000 : 0x2000;
519 if (page >= 4)
return unsigned(-1);
521 unsigned bank = bankRegsSubSlot1[page];
522 if (isDSKmodeEnabled() && (page == 0) && (bank == 0)) {
524 }
else if (isDSKmodeEnabled() && (page == 1) && (bank == 1)) {
530 unsigned tmp = (bank *
size) + (addr & (
size - 1));
531 return (tmp + 0x010000) & 0x7FFFFF;
534byte MegaFlashRomSCCPlusSD::readMemSubSlot1(
word addr, EmuTime::param time)
536 if (isKonamiSCCmapperConfigured()) {
537 SCCEnable enable = getSCCEnable();
538 if (((enable == EN_SCC) && (0x9800 <= addr) && (addr < 0xA000)) ||
539 ((enable == EN_SCCPLUS) && (0xB800 <= addr) && (addr < 0xC000))) {
540 byte val = scc.
readMem(narrow_cast<uint8_t>(addr & 0xFF), time);
545 unsigned flashAddr = getFlashAddrSubSlot1(addr);
546 return (flashAddr !=
unsigned(-1))
547 ? flash.
read(flashAddr)
551byte MegaFlashRomSCCPlusSD::peekMemSubSlot1(
word addr, EmuTime::param time)
const
553 if (isKonamiSCCmapperConfigured()) {
554 SCCEnable enable = getSCCEnable();
555 if (((enable == EN_SCC) && (0x9800 <= addr) && (addr < 0xA000)) ||
556 ((enable == EN_SCCPLUS) && (0xB800 <= addr) && (addr < 0xC000))) {
557 byte val = scc.
peekMem(narrow_cast<uint8_t>(addr & 0xFF), time);
562 unsigned flashAddr = getFlashAddrSubSlot1(addr);
563 return (flashAddr !=
unsigned(-1))
564 ? flash.
peek(flashAddr)
568const byte* MegaFlashRomSCCPlusSD::getReadCacheLineSubSlot1(
word addr)
const
570 if (isKonamiSCCmapperConfigured()) {
571 SCCEnable enable = getSCCEnable();
572 if (((enable == EN_SCC) && (0x9800 <= addr) && (addr < 0xA000)) ||
573 ((enable == EN_SCCPLUS) && (0xB800 <= addr) && (addr < 0xC000))) {
578 unsigned flashAddr = getFlashAddrSubSlot1(addr);
579 return (flashAddr !=
unsigned(-1))
584void MegaFlashRomSCCPlusSD::writeMemSubSlot1(
word addr,
byte value, EmuTime::param time)
587 unsigned flashAddr = getFlashAddrSubSlot1(addr);
596 if (!isConfigRegDisabled() && (addr == 0x7FFC)) {
598 updateConfigReg(value);
601 if (!isMapperRegisterDisabled() && (addr == 0x7FFF)) {
607 if (!areBankRegsAndOffsetRegsDisabled() && (addr == 0x7FFD)) {
609 offsetReg = (offsetReg & 0x300) | value;
613 if (!areBankRegsAndOffsetRegsDisabled() && (addr == 0x7FFE)) {
615 offsetReg = (offsetReg & 0xFF) + ((value & 0x3) << 8);
619 if (isKonamiSCCmapperConfigured()) {
621 if ((addr & 0xFFFE) == 0xBFFE) {
628 SCCEnable enable = getSCCEnable();
629 bool isRamSegment2 = ((sccMode & 0x24) == 0x24) ||
630 ((sccMode & 0x10) == 0x10);
631 bool isRamSegment3 = ((sccMode & 0x10) == 0x10);
632 if (((enable == EN_SCC) && !isRamSegment2 &&
633 (0x9800 <= addr) && (addr < 0xA000)) ||
634 ((enable == EN_SCCPLUS) && !isRamSegment3 &&
635 (0xB800 <= addr) && (addr < 0xC000))) {
636 scc.
writeMem(narrow_cast<uint8_t>(addr & 0xFF), value, time);
641 unsigned page8kB = (addr >> 13) - 2;
642 if (!areBankRegsAndOffsetRegsDisabled() && (page8kB < 4)) {
644 switch (mapperReg & 0xE0) {
647 if ((addr & 0x1800) == 0x1000) {
651 sccBanks[page8kB] = value;
654 byte mask = areKonamiMapperLimitsEnabled() ? 0x3F : 0xFF;
655 bankRegsSubSlot1[page8kB] = value & mask;
661 if (isWritingKonamiBankRegisterDisabled() && (addr < 0x6000)) {
670 if ((addr < 0x5000) || ((0x5800 <= addr) && (addr < 0x6000)))
break;
671 byte mask = areKonamiMapperLimitsEnabled() ? 0x1F : 0xFF;
672 bankRegsSubSlot1[page8kB] = value & mask;
679 bankRegsSubSlot1[page8kB] = value;
685 if ((0x6000 <= addr) && (addr < 0x8000)) {
686 byte bank = (addr >> 11) & 0x03;
687 bankRegsSubSlot1[bank] = value;
700 if ((0x6000 <= addr) && (addr < 0x6800)) {
701 bankRegsSubSlot1[0] = narrow_cast<uint8_t>(2 * value + 0);
702 bankRegsSubSlot1[1] = narrow_cast<uint8_t>(2 * value + 1);
705 if ((0x7000 <= addr) && (addr < 0x7800)) {
706 bankRegsSubSlot1[2] = narrow_cast<uint8_t>(2 * value + 0);
707 bankRegsSubSlot1[3] = narrow_cast<uint8_t>(2 * value + 1);
714 if (flashAddr !=
unsigned(-1)) {
715 writeToFlash(flashAddr, value);
719byte* MegaFlashRomSCCPlusSD::getWriteCacheLineSubSlot1(
word )
const
726unsigned MegaFlashRomSCCPlusSD::calcMemMapperAddress(
word address)
const
728 auto bank = memMapperRegs[address >> 14];
729 return ((bank & MEMORY_MAPPER_MASK) << 14) | (address & 0x3FFF);
732byte MegaFlashRomSCCPlusSD::readMemSubSlot2(
word addr)
735 return checkedRam->read(calcMemMapperAddress(addr));
738byte MegaFlashRomSCCPlusSD::peekMemSubSlot2(
word addr)
const
740 return checkedRam->peek(calcMemMapperAddress(addr));
743const byte* MegaFlashRomSCCPlusSD::getReadCacheLineSubSlot2(
word addr)
const
745 return checkedRam->getReadCacheLine(calcMemMapperAddress(addr));
748void MegaFlashRomSCCPlusSD::writeMemSubSlot2(
word addr,
byte value)
751 checkedRam->write(calcMemMapperAddress(addr), value);
754byte* MegaFlashRomSCCPlusSD::getWriteCacheLineSubSlot2(
word addr)
const
756 return checkedRam->getWriteCacheLine(calcMemMapperAddress(addr));
759byte MegaFlashRomSCCPlusSD::MapperIO::readIO(
word port, EmuTime::param time)
761 return peekIO(port, time);
764byte MegaFlashRomSCCPlusSD::MapperIO::peekIO(
word port, EmuTime::param )
const
766 return getSelectedSegment(port & 3) |
byte(~MEMORY_MAPPER_MASK);
769void MegaFlashRomSCCPlusSD::MapperIO::writeIO(
word port,
byte value, EmuTime::param )
771 mega.memMapperRegs[port & 3] = value & MEMORY_MAPPER_MASK;
772 mega.invalidateDeviceRWCache(0x4000 * (port & 0x03), 0x4000);
775byte MegaFlashRomSCCPlusSD::MapperIO::getSelectedSegment(
byte page)
const
777 return mega.memMapperRegs[page];
782unsigned MegaFlashRomSCCPlusSD::getFlashAddrSubSlot3(
unsigned addr)
const
784 unsigned page8kB = (addr >> 13) - 2;
785 return (bankRegsSubSlot3[page8kB] & 0x7f) * 0x2000 + (addr & 0x1fff) + 0x700000;
788byte MegaFlashRomSCCPlusSD::readMemSubSlot3(
word addr, EmuTime::param )
790 if (((bankRegsSubSlot3[0] & 0xC0) == 0x40) && ((0x4000 <= addr) && (addr < 0x6000))) {
792 return sdCard[selectedCard]->transfer(0xFF, (addr & 0x1000) != 0);
795 if ((0x4000 <= addr) && (addr < 0xC000)) {
797 unsigned flashAddr = getFlashAddrSubSlot3(addr);
798 return flash.
read(flashAddr);
805byte MegaFlashRomSCCPlusSD::peekMemSubSlot3(
word addr, EmuTime::param )
const
807 if ((0x4000 <= addr) && (addr < 0xC000)) {
809 unsigned flashAddr = getFlashAddrSubSlot3(addr);
810 return flash.
peek(flashAddr);
817const byte* MegaFlashRomSCCPlusSD::getReadCacheLineSubSlot3(
word addr)
const
819 if (((bankRegsSubSlot3[0] & 0xC0) == 0x40) && ((0x4000 <= addr) && (addr < 0x6000))) {
823 if ((0x4000 <= addr) && (addr < 0xC000)) {
825 unsigned flashAddr = getFlashAddrSubSlot3(addr);
832void MegaFlashRomSCCPlusSD::writeMemSubSlot3(
word addr,
byte value, EmuTime::param )
835 if (((bankRegsSubSlot3[0] & 0xC0) == 0x40) && ((0x4000 <= addr) && (addr < 0x6000))) {
836 if (addr >= 0x5800) {
837 selectedCard = value & 1;
840 sdCard[selectedCard]->transfer(value, (addr & 0x1000) != 0);
845 if ((0x4000 <= addr) && (addr < 0xC000)) {
846 unsigned flashAddr = getFlashAddrSubSlot3(addr);
847 writeToFlash(flashAddr, value);
851 if ((0x6000 <= addr) && (addr < 0x8000)) {
852 byte page8kB = (addr >> 11) & 0x03;
853 bankRegsSubSlot3[page8kB] = value;
858byte* MegaFlashRomSCCPlusSD::getWriteCacheLineSubSlot3(
word )
const
867 switch (port & 0xFF) {
869 if (!isPSGalsoMappedToNormalPorts())
return;
872 psgLatch = value & 0x0F;
876 if (!isPSGalsoMappedToNormalPorts())
return;
887template<
typename Archive>
891 ar.template serializeBase<MSXDevice>(*
this);
894 ar.serialize(
"flash", flash,
895 "subslotReg", subslotReg);
901 ar.serialize(
"scc", scc,
903 "sccBanks", sccBanks,
905 "psgLatch", psgLatch,
906 "configReg", configReg,
907 "mapperReg", mapperReg,
908 "offsetReg", offsetReg,
909 "bankRegsSubSlot1", bankRegsSubSlot1);
910 if constexpr (Archive::IS_LOADER) {
912 byte tmp = configReg;
914 updateConfigReg(tmp);
919 if (checkedRam) ar.serialize(
"ram", checkedRam->getUncheckedRam());
920 ar.serialize(
"memMapperRegs", memMapperRegs);
923 ar.serialize(
"bankRegsSubSlot3", bankRegsSubSlot3,
924 "selectedCard", selectedCard,
925 "sdCard0", *sdCard[0],
926 "sdCard1", *sdCard[1]);
void reset(EmuTime::param time)
void writeRegister(unsigned reg, uint8_t value, EmuTime::param time)
void write(size_t address, uint8_t value)
const uint8_t * getReadCacheLine(size_t address) const
void setVppWpPinLow(bool value)
Setting the Vpp/WP# pin LOW enables a certain kind of write protection of some sectors.
uint8_t peek(size_t address) const
uint8_t read(size_t address) const
This class keeps track of which bytes in the Ram have been written to.
const XMLElement * findChild(std::string_view name) const
void register_IO_Out(byte port, MSXDevice *device)
Devices can register their Out ports.
void unregister_IO_Out(byte port, MSXDevice *device)
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
static std::array< byte, 0x10000 > unmappedRead
static std::array< byte, 0x10000 > unmappedWrite
void invalidateDeviceRWCache()
Calls MSXCPUInterface::invalidateXXCache() for the specific (part of) the slot that this device is lo...
EmuTime::param getCurrentTime() const
MSXCPUInterface & getCPUInterface() const
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.
MegaFlashRomSCCPlusSD(const DeviceConfig &config)
byte * getWriteCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
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.
~MegaFlashRomSCCPlusSD() override
void setChipMode(ChipMode newMode)
void powerUp(EmuTime::param time)
uint8_t readMem(uint8_t address, EmuTime::param time)
void reset(EmuTime::param time)
uint8_t peekMem(uint8_t address, EmuTime::param time) const
void writeMem(uint8_t address, uint8_t value, EmuTime::param time)
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
std::string getName(KeyCode keyCode)
Translate key code to key name.
This file implemented 3 utility functions:
uint8_t byte
8 bit unsigned integer
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
AmdFlash::SectorInfo Info
uint16_t word
16 bit unsigned integer
constexpr void fill(ForwardRange &&range, const T &value)
constexpr void iota(ForwardIt first, ForwardIt last, T value)
size_t size(std::string_view utf8)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
constexpr auto xrange(T e)