openMSX
MusicalMemoryMapper.cc
Go to the documentation of this file.
2#include "enumerate.hh"
3#include "serialize.hh"
4#include "xrange.hh"
5
6namespace openmsx {
7
8static constexpr byte MEM_ACCESS_ENABLED = 1 << 7;
9static constexpr byte SOUND_PORT_ENABLED = 1 << 6;
10static constexpr byte PORT_ACCESS_DISABLED = 1 << 5;
11static constexpr byte UNUSED = 1 << 4;
12static constexpr byte WRITE_PROTECT = 0x0F;
13
15 : MSXMemoryMapperBase(config)
16 , sn76489(config)
17{
18}
19
20void MusicalMemoryMapper::reset(EmuTime::param time)
21{
22 controlReg = 0x00;
23
24 // MMM inits the page registers to 3, 2, 1, 0 instead of zeroes, so we
25 // don't call the superclass implementation.
26 for (auto [page, reg] : enumerate(registers)) {
27 reg = byte(3 - page);
28 }
29
30 // Note: The actual SN76489AN chip does not have a reset pin. I assume
31 // MMM either powers off the chip on reset or suppresses its audio
32 // output. We instead keep the chip in a silent state until it is
33 // activated.
34 sn76489.reset(time);
35}
36
37byte MusicalMemoryMapper::readIO(word port, EmuTime::param time)
38{
39 if (controlReg & PORT_ACCESS_DISABLED) {
40 return 0xFF;
41 } else {
42 return MSXMemoryMapperBase::readIO(port, time);
43 }
44}
45
46byte MusicalMemoryMapper::peekIO(word port, EmuTime::param time) const
47{
48 if (controlReg & PORT_ACCESS_DISABLED) {
49 return 0xFF;
50 } else {
51 return MSXMemoryMapperBase::peekIO(port, time);
52 }
53}
54
55void MusicalMemoryMapper::writeIO(word port, byte value, EmuTime::param time)
56{
57 if ((port & 0xFC) == 0xFC) {
58 // Mapper port.
59 if (!(controlReg & PORT_ACCESS_DISABLED)) {
60 MSXMemoryMapperBase::writeIOImpl(port, value, time);
61 invalidateDeviceRWCache(0x4000 * (port & 0x03), 0x4000);
62 }
63 } else if (port & 1) {
64 // Sound chip.
65 if (controlReg & SOUND_PORT_ENABLED) {
66 sn76489.write(value, time);
67 }
68 } else {
69 // Control port.
70 // Only bit 7 of the control register is accessible through this port.
71 // The documentation explicitly states that the sound chip port is still
72 // accessible when port access is disabled, but this control port must
73 // also remain accessible or the ROM2MMM loader won't work.
74 updateControlReg((value & MEM_ACCESS_ENABLED) |
75 (controlReg & ~MEM_ACCESS_ENABLED));
76 }
77}
78
79bool MusicalMemoryMapper::registerAccessAt(word address) const
80{
81 if (controlReg & MEM_ACCESS_ENABLED) {
82 if (controlReg & PORT_ACCESS_DISABLED) {
83 // Note: I'm assuming the mirroring is still active when the
84 // address range is restricted, but this is unclear in
85 // the documentation and not tested on real hardware.
86 if (controlReg & 0x08) {
87 return 0x4000 <= address && address < 0x8000;
88 } else {
89 return 0x8000 <= address && address < 0xC000;
90 }
91 } else {
92 return 0x4000 <= address && address < 0xC000;
93 }
94 } else {
95 return false;
96 }
97}
98
99int MusicalMemoryMapper::readReg(word address) const
100{
101 if (registerAccessAt(address)) {
102 switch (address & 0xFF) {
103 case 0x3C:
104 return controlReg;
105 case 0xFC:
106 case 0xFD:
107 case 0xFE:
108 case 0xFF:
109 return 0xC0 | getSelectedSegment(address & 0x03);
110 }
111 }
112 return -1;
113}
114
115void MusicalMemoryMapper::updateControlReg(byte value)
116{
117 // Note: Bit 4 is unused, but I don't know its value when read.
118 // For now, I'll force it to 0.
119 value &= ~UNUSED;
120
121 byte change = value ^ controlReg;
122 if (change) {
123 // Pages for which write protect is toggled must be invalidated.
124 byte invalidate = change & WRITE_PROTECT;
125
126 // Invalidate pages for which register access changes.
127 byte regAccessBefore = 0;
128 for (auto page : xrange(4)) {
129 regAccessBefore |= registerAccessAt(0x4000 * page) << page;
130 }
131 controlReg = value;
132 byte regAccessAfter = 0;
133 for (auto page : xrange(4)) {
134 regAccessAfter |= registerAccessAt(0x4000 * page) << page;
135 }
136 invalidate |= regAccessBefore ^ regAccessAfter;
137
138 for (auto page : xrange(4)) {
139 if ((invalidate >> page) & 1) {
140 invalidateDeviceRWCache(0x4000 * page, 0x4000);
141 }
142 }
143 }
144}
145
146byte MusicalMemoryMapper::peekMem(word address, EmuTime::param time) const
147{
148 int reg = readReg(address);
149 return reg >= 0 ? reg : MSXMemoryMapperBase::peekMem(address, time);
150}
151
152byte MusicalMemoryMapper::readMem(word address, EmuTime::param time)
153{
154 int reg = readReg(address);
155 return reg >= 0 ? reg : MSXMemoryMapperBase::readMem(address, time);
156}
157
158void MusicalMemoryMapper::writeMem(word address, byte value, EmuTime::param time)
159{
160 if (registerAccessAt(address)) {
161 switch (address & 0xFF) {
162 case 0x3C:
163 // When port access is disabled, memory access is automatically
164 // enabled, according to the manual.
165 if (value & PORT_ACCESS_DISABLED) {
166 value |= MEM_ACCESS_ENABLED;
167 }
168 updateControlReg(value);
169 return;
170 case 0xFC:
171 case 0xFD:
172 case 0xFE:
173 case 0xFF:
174 MSXMemoryMapperBase::writeIOImpl(address & 0xFF, value, time);
175 invalidateDeviceRWCache(0x4000 * (address & 0x03), 0x4000);
176 return;
177 }
178 }
179 if (!writeProtected(address)) {
180 MSXMemoryMapperBase::writeMem(address, value, time);
181 }
182}
183
185{
186 if (controlReg & MEM_ACCESS_ENABLED) {
187 if (0x4000 <= start && start < 0xC000) {
188 return nullptr;
189 }
190 }
192}
193
195{
196 if (controlReg & MEM_ACCESS_ENABLED) {
197 if (0x4000 <= start && start < 0xC000) {
198 return nullptr;
199 }
200 }
201 if (writeProtected(start)) {
202 return unmappedWrite.data();
203 } else {
205 }
206}
207
208template<typename Archive>
209void MusicalMemoryMapper::serialize(Archive& ar, unsigned /*version*/)
210{
211 ar.template serializeBase<MSXMemoryMapperBase>(*this);
212 ar.serialize("ctrl", controlReg,
213 "sn76489", sn76489);
214}
217
218} // namespace openmsx
static std::array< byte, 0x10000 > unmappedWrite
Definition: MSXDevice.hh:303
void invalidateDeviceRWCache()
Calls MSXCPUInterface::invalidateXXCache() for the specific (part of) the slot that this device is lo...
Definition: MSXDevice.hh:210
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
void writeIOImpl(word port, byte value, EmuTime::param time)
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
std::array< byte, 4 > registers
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.
byte getSelectedSegment(byte page) const override
Returns the currently selected segment for the given page.
const byte * getReadCacheLine(word start) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading.
byte * getWriteCacheLine(word start) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
Memory mapper which also controls an SN76489AN sound chip.
byte * getWriteCacheLine(word start) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
void reset(EmuTime::param time) override
This method is called on reset.
const byte * getReadCacheLine(word start) 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 serialize(Archive &ar, unsigned version)
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.
MusicalMemoryMapper(const DeviceConfig &config)
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
void reset(EmuTime::param time)
Definition: SN76489.cc:141
void write(byte value, EmuTime::param time)
Definition: SN76489.cc:147
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
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1021
constexpr auto xrange(T e)
Definition: xrange.hh:133