diff --git a/build/msvc/openmsx.vcxproj b/build/msvc/openmsx.vcxproj index 4520a5e354..d9ba7194bc 100644 --- a/build/msvc/openmsx.vcxproj +++ b/build/msvc/openmsx.vcxproj @@ -555,6 +555,7 @@ + @@ -1134,6 +1135,7 @@ + diff --git a/build/msvc/openmsx.vcxproj.filters b/build/msvc/openmsx.vcxproj.filters index 3aa4c95869..43ec0792b3 100644 --- a/build/msvc/openmsx.vcxproj.filters +++ b/build/msvc/openmsx.vcxproj.filters @@ -765,6 +765,9 @@ memory + + memory + memory @@ -2257,6 +2260,9 @@ memory + + memory + memory diff --git a/share/extensions/ASCII16-X_Mapper_XL.xml b/share/extensions/ASCII16-X_Mapper_XL.xml new file mode 100644 index 0000000000..6392918e68 --- /dev/null +++ b/share/extensions/ASCII16-X_Mapper_XL.xml @@ -0,0 +1,25 @@ + + + + + ASCII16-X Mapper XL + Grauw + ASCII16-X + 2024 + ASCII16-X Mapper Extra Large (XL) 8 MB + Flash cartridge + + + + + + + + 8192 + + ASCII16-X_Mapper_XL.sram + + + + + diff --git a/src/DeviceFactory.cc b/src/DeviceFactory.cc index dd02d7b832..d97ea033cd 100644 --- a/src/DeviceFactory.cc +++ b/src/DeviceFactory.cc @@ -78,6 +78,7 @@ #include "DebugDevice.hh" #include "V9990.hh" #include "Video9000.hh" +#include "RomAscii16X.hh" #include "ADVram.hh" #include "NowindInterface.hh" #include "MSXMirrorDevice.hh" @@ -307,6 +308,8 @@ std::unique_ptr DeviceFactory::create(const DeviceConfig& conf) result = make_unique(conf); } else if (type == "Carnivore2") { result = make_unique(conf); + } else if (type == "ASCII16-X") { + result = make_unique(conf); } else if (type == "YamahaSKW01") { result = make_unique(conf); } else if (type == one_of("T7775", "T7937", "T9763", "T9769")) { diff --git a/src/memory/AmdFlash.hh b/src/memory/AmdFlash.hh index 429ad5a9c5..4522fd076c 100644 --- a/src/memory/AmdFlash.hh +++ b/src/memory/AmdFlash.hh @@ -258,7 +258,7 @@ private: [[nodiscard]] bool isSectorWritable(size_t sector) const; public: - static constexpr unsigned MAX_CMD_SIZE = 5 + 32; // longest command is BufferProgram + static constexpr unsigned MAX_CMD_SIZE = 5 + 256; // longest command is BufferProgram private: MSXMotherBoard& motherBoard; @@ -314,6 +314,19 @@ namespace AmdFlashChip .primaryAlgorithm{{1, 3}, 0, 0, 2, 4, 1, 4, 0, 0, 1, {0xB5, 0xC5}, 0x02, 1}, }, }}; + + // Infineon / Cypress / Spansion S29GL064S70TFI040 + static constexpr ValidatedChip S29GL064S70TFI040 = {{ + .autoSelect{.manufacturer = AMD, .device{0x10, 0x00}, .extraCode = 0xFF0A, .readMask = 0x0F}, + .geometry{DeviceInterface::x8x16, {{8, 0x2000}, {127, 0x10000}}}, + .program{.bufferCommand = true, .pageSize = 256}, + .cfi{ + .command = true, .withAutoSelect = true, .exitCommand = true, .commandMask = 0xFF, .readMask = 0x7F, + .systemInterface{{0x27, 0x36, 0x00, 0x00}, {256, 256, 512, 65536}, {8, 8, 2, 1}}, + .primaryAlgorithm{{1, 3}, 0, 8, 2, 1, 0, 8, 0, 0, 2, {0xB5, 0xC5}, 0x02, 1}, + }, + .misc {.statusCommand = true, .continuityCommand = true}, + }}; } } // namespace openmsx diff --git a/src/memory/RomAscii16X.cc b/src/memory/RomAscii16X.cc new file mode 100644 index 0000000000..865154ca6f --- /dev/null +++ b/src/memory/RomAscii16X.cc @@ -0,0 +1,120 @@ +// ASCII-X 16kB cartridges +// +// Banks: +// first 16kB: 0x4000 - 0x7FFF and 0xC000 - 0xFFFF +// second 16kB: 0x8000 - 0xBFFF and 0x0000 - 0x3FFF +// +// Address to change banks: +// first 16kB: 0x6000 - 0x6FFF, 0xA000 - 0xAFFF, 0xE000 - 0xEFFF, 0x2000 - 0x2FFF +// second 16kB: 0x7000 - 0x7FFF, 0xB000 - 0xBFFF, 0xF000 - 0xFFFF, 0x3000 - 0x3FFF +// +// 12-bit bank number is composed by address bits 8-11 and the data bits. +// +// Backed by FlashROM. + +#include "RomAscii16X.hh" +#include "narrow.hh" +#include "outer.hh" +#include "serialize.hh" + +namespace openmsx { + +RomAscii16X::RomAscii16X(const DeviceConfig& config) + : RomAscii16X(config, Rom(std::string(config.getAttributeValue("id")), "ASCII16-X", config)) +{ +} + +RomAscii16X::RomAscii16X(const DeviceConfig& config, Rom&& rom_) + : MSXRom(config, std::move(rom_)) + , debuggable(getMotherBoard(), getName()) + , flash(rom, AmdFlashChip::S29GL064S70TFI040, {}, config) +{ +} + +void RomAscii16X::reset(EmuTime::param /* time */) +{ + ranges::iota(bankRegs, word(0)); + + flash.reset(); + + invalidateDeviceRCache(); // flush all to be sure +} + +unsigned RomAscii16X::getFlashAddr(word addr) const +{ + word bank = bankRegs[((addr >> 14) & 1) ^ 1]; + return (bank << 14) | (addr & 0x3FFF); +} + +byte RomAscii16X::readMem(word addr, EmuTime::param /* time */) +{ + return flash.read(getFlashAddr(addr)); +} + +byte RomAscii16X::peekMem(word addr, EmuTime::param /* time */) const +{ + return flash.peek(getFlashAddr(addr)); +} + +const byte* RomAscii16X::getReadCacheLine(word addr) const +{ + return flash.getReadCacheLine(getFlashAddr(addr)); +} + +void RomAscii16X::writeMem(word addr, byte value, EmuTime::param /* time */) +{ + flash.write(getFlashAddr(addr), value); + + if ((addr & 0x3FFF) >= 0x2000) { + const word index = (addr >> 12) & 1; + bankRegs[index] = (addr & 0x0F00) | value; + invalidateDeviceRCache(0x4000 ^ (index << 14), 0x4000); + invalidateDeviceRCache(0xC000 ^ (index << 14), 0x4000); + } +} + +byte* RomAscii16X::getWriteCacheLine(word /* addr */) +{ + return nullptr; // not cacheable +} + +RomAscii16X::Debuggable::Debuggable(MSXMotherBoard& motherBoard_, + const std::string& name_) + : SimpleDebuggable(motherBoard_, name_ + " regs", "ASCII16-X bank registers", 4) +{ +} + +byte RomAscii16X::Debuggable::read(unsigned address) +{ + auto& outer = OUTER(RomAscii16X, debuggable); + word bank = outer.bankRegs[(address >> 1) & 1]; + return narrow(address & 1 ? bank >> 8 : bank & 0xFF); +} + +void RomAscii16X::Debuggable::write(unsigned address, byte value, + EmuTime::param /* time */) +{ + auto& outer = OUTER(RomAscii16X, debuggable); + word& bank = outer.bankRegs[(address >> 1) & 1]; + if (address & 1) { + bank = (bank & 0x00FF) | narrow((value << 8) & 0x0F00); + } else { + bank = (bank & 0x0F00) | value; + } + + outer.invalidateDeviceRCache(); +} + +template +void RomAscii16X::serialize(Archive& ar, unsigned /*version*/) +{ + // skip MSXRom base class + ar.template serializeBase(*this); + + ar.serialize("flash", flash, + "bankRegs", bankRegs); +} +INSTANTIATE_SERIALIZE_METHODS(RomAscii16X); +REGISTER_MSXDEVICE(RomAscii16X, "RomAscii16X"); + +} // namespace openmsx diff --git a/src/memory/RomAscii16X.hh b/src/memory/RomAscii16X.hh new file mode 100644 index 0000000000..84be941840 --- /dev/null +++ b/src/memory/RomAscii16X.hh @@ -0,0 +1,43 @@ +#ifndef ROMASCII16X_HH +#define ROMASCII16X_HH + +#include "MSXRom.hh" +#include "AmdFlash.hh" +#include "SimpleDebuggable.hh" +#include + +namespace openmsx { + +class RomAscii16X final : public MSXRom +{ +public: + explicit RomAscii16X(const DeviceConfig& config); + RomAscii16X(const DeviceConfig& config, Rom&& rom); + + void reset(EmuTime::param time) override; + [[nodiscard]] byte peekMem(word address, EmuTime::param time) const override; + [[nodiscard]] byte readMem(word address, EmuTime::param time) override; + [[nodiscard]] const byte* getReadCacheLine(word address) const override; + void writeMem(word address, byte value, EmuTime::param time) override; + [[nodiscard]] byte* getWriteCacheLine(word address) override; + + template + void serialize(Archive& ar, unsigned version); + +private: + [[nodiscard]] unsigned getFlashAddr(word addr) const; + + struct Debuggable final : SimpleDebuggable { + Debuggable(MSXMotherBoard& motherBoard, const std::string& name); + [[nodiscard]] byte read(unsigned address) override; + void write(unsigned address, byte value, EmuTime::param time) override; + } debuggable; + + AmdFlash flash; + + std::array bankRegs = {0, 0}; +}; + +} // namespace openmsx + +#endif diff --git a/src/memory/RomFactory.cc b/src/memory/RomFactory.cc index 0e750927a2..c518809cde 100644 --- a/src/memory/RomFactory.cc +++ b/src/memory/RomFactory.cc @@ -13,6 +13,7 @@ #include "RomAscii8kB.hh" #include "RomAscii8_8.hh" #include "RomAscii16kB.hh" +#include "RomAscii16X.hh" #include "RomMSXWrite.hh" #include "RomPadial8kB.hh" #include "RomPadial16kB.hh" @@ -88,6 +89,7 @@ using enum RomType; if (const size_t signatureOffset = 16, signatureSize = 8; size >= (signatureOffset + signatureSize)) { auto signature = std::string_view(std::bit_cast(data.data()) + signatureOffset, signatureSize); + if (signature == std::string_view("ASCII16X")) return ASCII16X; if (signature == std::string_view("ROM_NEO8")) return NEO8; if (signature == std::string_view("ROM_NE16")) return NEO16; } @@ -267,6 +269,9 @@ std::unique_ptr create(const DeviceConfig& config) case ASCII16: result = make_unique(config, std::move(rom)); break; + case ASCII16X: + result = make_unique(config, std::move(rom)); + break; case MSXWRITE: result = make_unique(config, std::move(rom)); break; diff --git a/src/memory/RomInfo.cc b/src/memory/RomInfo.cc index 799da34735..50bc0b6520 100644 --- a/src/memory/RomInfo.cc +++ b/src/memory/RomInfo.cc @@ -26,6 +26,7 @@ static constexpr auto romTypeInfoArray = [] { r[KBDMASTER] = {0x4000, "KeyboardMaster", "Konami Keyboard Master with VLM5030"}; // officially plain 16K r[ASCII8] = {0x2000, "ASCII8", "ASCII 8kB"}; r[ASCII16] = {0x4000, "ASCII16", "ASCII 16kB"}; + r[ASCII16X] = {0x4000, "ASCII16-X", "ASCII-X 16kB"}; r[R_TYPE] = {0x4000, "R-Type", "R-Type"}; r[CROSS_BLAIM] = {0x4000, "CrossBlaim", "Cross Blaim"}; r[HARRY_FOX] = {0x4000, "HarryFox", "Harry Fox"}; diff --git a/src/memory/RomTypes.hh b/src/memory/RomTypes.hh index e12ceff66f..89b66a0f3b 100644 --- a/src/memory/RomTypes.hh +++ b/src/memory/RomTypes.hh @@ -14,6 +14,7 @@ enum class RomType { ASCII16, ASCII16_2, ASCII16_8, + ASCII16X, COLECOMEGACART, CROSS_BLAIM, DOOLY, diff --git a/src/meson.build b/src/meson.build index 69ebd305fa..103cd8030c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -267,6 +267,7 @@ sources = files( 'memory/RomArc.cc', 'memory/RomAscii16_2.cc', 'memory/RomAscii16kB.cc', + 'memory/RomAscii16X.cc', 'memory/RomAscii8_8.cc', 'memory/RomAscii8kB.cc', 'memory/RomBlocks.cc',