| e8555c89 Andy McCall |
1 |
#include "MSXMemoryMapperBase.hh" |
| 2 |
|
| 3 |
#include "MSXException.hh" |
| 4 |
|
| 5 |
#include "outer.hh" |
| 6 |
#include "serialize.hh" |
| 7 |
|
| 8 |
#include <algorithm> |
| 9 |
#include <bit> |
| 10 |
|
| 11 |
namespace openmsx { |
| 12 |
|
| 13 |
[[nodiscard]] static unsigned getRamSize(const DeviceConfig& config) |
| 14 |
{ |
| 15 |
int kSize = config.getChildDataAsInt("size", 0); |
| 16 |
if ((kSize % 16) != 0) { |
| 17 |
throw MSXException("Mapper size is not a multiple of 16K: ", kSize); |
| 18 |
} |
| 19 |
if (kSize <= 0) { |
| 20 |
throw MSXException("Mapper size must be at least 16kB: ", kSize); |
| 21 |
} |
| 22 |
if (kSize > 4096) { |
| 23 |
throw MSXException("Mapper size must not be larger than 4096kB: ", kSize); |
| 24 |
} |
| 25 |
return kSize * 1024; |
| 26 |
} |
| 27 |
|
| 28 |
MSXMemoryMapperBase::MSXMemoryMapperBase(const DeviceConfig& config) |
| 29 |
: MSXDevice(config) |
| 30 |
, MSXMapperIOClient(getMotherBoard()) |
| 31 |
, checkedRam(config, getName(), "memory mapper", getRamSize(config)) |
| 32 |
, debuggable(getMotherBoard(), getName()) |
| 33 |
{ |
| 34 |
powerUp(getCurrentTime()); |
| 35 |
} |
| 36 |
|
| 37 |
unsigned MSXMemoryMapperBase::getBaseSizeAlignment() const |
| 38 |
{ |
| 39 |
return 0x4000; |
| 40 |
} |
| 41 |
|
| 42 |
void MSXMemoryMapperBase::powerUp(EmuTime time) |
| 43 |
{ |
| 44 |
checkedRam.clear(); |
| 45 |
reset(time); |
| 46 |
} |
| 47 |
|
| 48 |
void MSXMemoryMapperBase::reset(EmuTime ) |
| 49 |
{ |
| 50 |
|
| 51 |
|
| 52 |
std::ranges::fill(registers, 0); |
| 53 |
} |
| 54 |
|
| 55 |
byte MSXMemoryMapperBase::readIO(uint16_t port, EmuTime time) |
| 56 |
{ |
| 57 |
return peekIO(port, time); |
| 58 |
} |
| 59 |
|
| 60 |
byte MSXMemoryMapperBase::peekIO(uint16_t port, EmuTime ) const |
| 61 |
{ |
| 62 |
auto numSegments = narrow<unsigned>(checkedRam.size() / 0x4000); |
| 63 |
return registers[port & 0x03] | byte(~(std::bit_ceil(numSegments) - 1)); |
| 64 |
} |
| 65 |
|
| 66 |
void MSXMemoryMapperBase::writeIOImpl(uint16_t port, byte value, EmuTime ) |
| 67 |
{ |
| 68 |
auto numSegments = narrow<unsigned>(checkedRam.size() / 0x4000); |
| 69 |
registers[port & 3] = value & byte(std::bit_ceil(numSegments) - 1); |
| 70 |
|
| 71 |
} |
| 72 |
|
| 73 |
unsigned MSXMemoryMapperBase::segmentOffset(byte page) const |
| 74 |
{ |
| 75 |
unsigned segment = registers[page]; |
| 76 |
auto numSegments = narrow<unsigned>(checkedRam.size() / 0x4000); |
| 77 |
segment = (segment < numSegments) ? segment : segment & (numSegments - 1); |
| 78 |
return segment * 0x4000; |
| 79 |
} |
| 80 |
|
| 81 |
unsigned MSXMemoryMapperBase::calcAddress(uint16_t address) const |
| 82 |
{ |
| 83 |
return segmentOffset(narrow<byte>(address / 0x4000)) | (address & 0x3fff); |
| 84 |
} |
| 85 |
|
| 86 |
byte MSXMemoryMapperBase::peekMem(uint16_t address, EmuTime ) const |
| 87 |
{ |
| 88 |
return checkedRam.peek(calcAddress(address)); |
| 89 |
} |
| 90 |
|
| 91 |
byte MSXMemoryMapperBase::readMem(uint16_t address, EmuTime ) |
| 92 |
{ |
| 93 |
return checkedRam.read(calcAddress(address)); |
| 94 |
} |
| 95 |
|
| 96 |
void MSXMemoryMapperBase::writeMem(uint16_t address, byte value, EmuTime ) |
| 97 |
{ |
| 98 |
checkedRam.write(calcAddress(address), value); |
| 99 |
} |
| 100 |
|
| 101 |
const byte* MSXMemoryMapperBase::getReadCacheLine(uint16_t start) const |
| 102 |
{ |
| 103 |
return checkedRam.getReadCacheLine(calcAddress(start)); |
| 104 |
} |
| 105 |
|
| 106 |
byte* MSXMemoryMapperBase::getWriteCacheLine(uint16_t start) |
| 107 |
{ |
| 108 |
return checkedRam.getWriteCacheLine(calcAddress(start)); |
| 109 |
} |
| 110 |
|
| 111 |
|
| 112 |
|
| 113 |
|
| 114 |
MSXMemoryMapperBase::Debuggable::Debuggable(MSXMotherBoard& motherBoard_, |
| 115 |
const std::string& name_) |
| 116 |
: SimpleDebuggable(motherBoard_, name_ + " regs", |
| 117 |
"Memory mapper registers", 4) |
| 118 |
{ |
| 119 |
} |
| 120 |
|
| 121 |
byte MSXMemoryMapperBase::Debuggable::read(unsigned address) |
| 122 |
{ |
| 123 |
auto& mapper = OUTER(MSXMemoryMapperBase, debuggable); |
| 124 |
return mapper.registers[address]; |
| 125 |
} |
| 126 |
|
| 127 |
void MSXMemoryMapperBase::Debuggable::readBlock(unsigned start, std::span<byte> output) |
| 128 |
{ |
| 129 |
auto& mapper = OUTER(MSXMemoryMapperBase, debuggable); |
| 130 |
copy_to_range(std::span{mapper.registers}.subspan(start, output.size()), output); |
| 131 |
} |
| 132 |
|
| 133 |
void MSXMemoryMapperBase::Debuggable::write(unsigned address, byte value) |
| 134 |
{ |
| 135 |
auto& mapper = OUTER(MSXMemoryMapperBase, debuggable); |
| 136 |
mapper.writeIO(narrow<uint16_t>(address), value, EmuTime::dummy()); |
| 137 |
} |
| 138 |
|
| 139 |
|
| 140 |
template<typename Archive> |
| 141 |
void MSXMemoryMapperBase::serialize(Archive& ar, unsigned version) |
| 142 |
{ |
| 143 |
ar.template serializeBase<MSXDevice>(*this); |
| 144 |
if (ar.versionAtLeast(version, 2)) { |
| 145 |
ar.serialize("registers", registers); |
| 146 |
} |
| 147 |
|
| 148 |
ar.serialize("ram", checkedRam.getUncheckedRam()); |
| 149 |
} |
| 150 |
INSTANTIATE_SERIALIZE_METHODS(MSXMemoryMapperBase); |
| 151 |
|
| 152 |
|
| 153 |
} |