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