openMSX
RomManbow2.cc
Go to the documentation of this file.
1#include "RomManbow2.hh"
2#include "AY8910.hh"
4#include "SCC.hh"
5#include "MSXCPUInterface.hh"
6#include "narrow.hh"
7#include "one_of.hh"
8#include "ranges.hh"
9#include "serialize.hh"
10#include "unreachable.hh"
11#include "xrange.hh"
12#include <array>
13#include <cassert>
14#include <memory>
15
16namespace openmsx {
17
19
20// 512kB, only last 64kB writable
21static constexpr auto config1 = [] {
22 std::array<Info, 512 / 64> result = {};
23 ranges::fill(result, Info{64 * 1024, true}); // read-only
24 result[7].writeProtected = false;
25 return result;
26}();
27// 512kB, only 128kB writable
28static constexpr auto config2 = [] {
29 std::array<Info, 512 / 64> result = {};
30 ranges::fill(result, Info{64 * 1024, true}); // read-only
31 result[4].writeProtected = false;
32 result[5].writeProtected = false;
33 return result;
34}();
35// fully writeable, 512kB
36static constexpr auto config3 = [] {
37 std::array<Info, 512 / 64> result = {};
38 ranges::fill(result, Info{64 * 1024, false});
39 return result;
40}();
41// fully writeable, 2MB
42static constexpr auto config4 = [] {
43 std::array<Info, 2048 / 64> result = {};
44 ranges::fill(result, Info{64 * 1024, false});
45 return result;
46}();
47[[nodiscard]] static constexpr std::span<const Info> getSectorInfo(RomType type)
48{
49 switch (type) {
50 case ROM_MANBOW2:
51 case ROM_MANBOW2_2:
52 return config1;
54 return config2;
56 return config3;
58 return config4;
59 default:
61 }
62}
63
65 RomType type)
66 : MSXRom(config, std::move(rom_))
67 , scc((type != ROM_RBSC_FLASH_KONAMI_SCC)
68 ? std::make_unique<SCC>(
69 getName() + " SCC", config, getCurrentTime())
70 : nullptr)
71 , psg((type == one_of(ROM_MANBOW2_2, ROM_HAMARAJANIGHT))
72 ? std::make_unique<AY8910>(
73 getName() + " PSG", DummyAY8910Periphery::instance(),
74 config, getCurrentTime())
75 : nullptr)
76 , flash(rom, getSectorInfo(type),
77 type == ROM_RBSC_FLASH_KONAMI_SCC ? 0x01AD : 0x01A4,
78 AmdFlash::Addressing::BITS_11, config)
79 , romBlockDebug(*this, bank, 0x4000, 0x8000, 13)
80{
82
83 if (psg) {
84 auto& cpuInterface = getCPUInterface();
85 for (auto port : {0x10, 0x11}) {
86 cpuInterface.register_IO_Out(narrow_cast<byte>(port), this);
87 }
88 cpuInterface.register_IO_In(0x12, this);
89 }
90}
91
93{
94 if (psg) {
95 auto& cpuInterface = getCPUInterface();
96 for (auto port : {0x10, 0x11}) {
97 cpuInterface.unregister_IO_Out(narrow_cast<byte>(port), this);
98 }
99 cpuInterface.unregister_IO_In(0x12, this);
100 }
101}
102
103void RomManbow2::powerUp(EmuTime::param time)
104{
105 if (scc) {
106 scc->powerUp(time);
107 }
108 reset(time);
109}
110
111void RomManbow2::reset(EmuTime::param time)
112{
113 for (auto i : xrange(4)) {
114 setRom(i, byte(i));
115 }
116
117 sccEnabled = false;
118 if (scc) {
119 scc->reset(time);
120 }
121
122 if (psg) {
123 psgLatch = 0;
124 psg->reset(time);
125 }
126
127 flash.reset();
128}
129
130void RomManbow2::setRom(unsigned region, byte block)
131{
132 assert(region < 4);
133 auto nrBlocks = narrow<unsigned>(flash.size() / 0x2000);
134 bank[region] = block & narrow<byte>(nrBlocks - 1);
135 invalidateDeviceRCache(0x4000 + region * 0x2000, 0x2000);
136}
137
138byte RomManbow2::peekMem(word address, EmuTime::param time) const
139{
140 if (sccEnabled && (0x9800 <= address) && (address < 0xA000)) {
141 return scc->peekMem(narrow_cast<uint8_t>(address & 0xFF), time);
142 } else if ((0x4000 <= address) && (address < 0xC000)) {
143 unsigned page = (address - 0x4000) / 0x2000;
144 unsigned addr = (address & 0x1FFF) + 0x2000 * bank[page];
145 return flash.peek(addr);
146 } else {
147 return 0xFF;
148 }
149}
150
151byte RomManbow2::readMem(word address, EmuTime::param time)
152{
153 if (sccEnabled && (0x9800 <= address) && (address < 0xA000)) {
154 return scc->readMem(narrow_cast<uint8_t>(address & 0xFF), time);
155 } else if ((0x4000 <= address) && (address < 0xC000)) {
156 unsigned page = (address - 0x4000) / 0x2000;
157 unsigned addr = (address & 0x1FFF) + 0x2000 * bank[page];
158 return flash.read(addr);
159 } else {
160 return 0xFF;
161 }
162}
163
164const byte* RomManbow2::getReadCacheLine(word address) const
165{
166 if (sccEnabled && (0x9800 <= address) && (address < 0xA000)) {
167 return nullptr;
168 } else if ((0x4000 <= address) && (address < 0xC000)) {
169 unsigned page = (address - 0x4000) / 0x2000;
170 unsigned addr = (address & 0x1FFF) + 0x2000 * bank[page];
171 return flash.getReadCacheLine(addr);
172 } else {
173 return unmappedRead.data();
174 }
175}
176
177void RomManbow2::writeMem(word address, byte value, EmuTime::param time)
178{
179 if (sccEnabled && (0x9800 <= address) && (address < 0xA000)) {
180 // write to SCC
181 scc->writeMem(narrow_cast<uint8_t>(address & 0xff), value, time);
182 // note: writes to SCC also go to flash
183 // thanks to 'enen' for testing this
184 }
185 if ((0x4000 <= address) && (address < 0xC000)) {
186 unsigned page = (address - 0x4000) / 0x2000;
187 unsigned addr = (address & 0x1FFF) + 0x2000 * bank[page];
188 flash.write(addr, value);
189
190 if (scc && ((address & 0xF800) == 0x9000)) {
191 // SCC enable/disable
192 sccEnabled = ((value & 0x3F) == 0x3F);
193 invalidateDeviceRCache(0x9800, 0x0800);
194 }
195 if ((address & 0x1800) == 0x1000) {
196 // page selection
197 setRom(page, value);
198 }
199 }
200}
201
203{
204 if ((0x4000 <= address) && (address < 0xC000)) {
205 return nullptr;
206 } else {
207 return unmappedWrite.data();
208 }
209}
210
211byte RomManbow2::readIO(word port, EmuTime::param time)
212{
213 assert((port & 0xFF) == 0x12); (void)port;
214 return psg->readRegister(psgLatch, time);
215}
216
217byte RomManbow2::peekIO(word port, EmuTime::param time) const
218{
219 assert((port & 0xFF) == 0x12); (void)port;
220 return psg->peekRegister(psgLatch, time);
221}
222
223void RomManbow2::writeIO(word port, byte value, EmuTime::param time)
224{
225 if ((port & 0xFF) == 0x10) {
226 psgLatch = value & 0x0F;
227 } else {
228 assert((port & 0xFF) == 0x11);
229 psg->writeRegister(psgLatch, value, time);
230 }
231}
232
233
234// version 1: initial version
235// version 2: added optional built-in PSG
236// version 3: made SCC optional (for ROM_RBSC_FLASH_KONAMI_SCC)
237template<typename Archive>
238void RomManbow2::serialize(Archive& ar, unsigned version)
239{
240 // skip MSXRom base class
241 ar.template serializeBase<MSXDevice>(*this);
242
243 if (scc) {
244 ar.serialize("scc", *scc,
245 "sccEnabled", sccEnabled);
246 }
247 if ((ar.versionAtLeast(version, 2)) && psg) {
248 ar.serialize("psg", *psg,
249 "psgLatch", psgLatch);
250 }
251 ar.serialize("flash", flash,
252 "bank", bank);
253}
256
257} // namespace openmsx
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition MSXDevice.hh:354
This class implements the AY-3-8910 sound chip.
Definition AY8910.hh:22
void write(size_t address, uint8_t value)
Definition AmdFlash.cc:265
const uint8_t * getReadCacheLine(size_t address) const
Definition AmdFlash.cc:254
size_t size() const
Definition AmdFlash.hh:67
uint8_t peek(size_t address) const
Definition AmdFlash.cc:212
uint8_t read(size_t address) const
Definition AmdFlash.cc:248
void invalidateDeviceRCache()
Definition MSXDevice.hh:213
static std::array< byte, 0x10000 > unmappedRead
Definition MSXDevice.hh:304
static std::array< byte, 0x10000 > unmappedWrite
Definition MSXDevice.hh:305
EmuTime::param getCurrentTime() const
Definition MSXDevice.cc:125
MSXCPUInterface & getCPUInterface() const
Definition MSXDevice.cc:133
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
const byte * getReadCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading.
~RomManbow2() override
Definition RomManbow2.cc:92
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.
RomManbow2(const DeviceConfig &config, Rom &&rom, RomType type)
Definition RomManbow2.cc:64
void reset(EmuTime::param time) override
This method is called on reset.
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
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 readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
void serialize(Archive &ar, unsigned version)
byte * getWriteCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
This file implemented 3 utility functions:
Definition Autofire.cc:11
AmdFlash::SectorInfo Info
Definition RomManbow2.cc:18
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
@ ROM_MANBOW2_2
Definition RomTypes.hh:41
@ ROM_HAMARAJANIGHT
Definition RomTypes.hh:27
@ ROM_MANBOW2
Definition RomTypes.hh:40
@ ROM_MEGAFLASHROMSCC
Definition RomTypes.hh:45
@ ROM_RBSC_FLASH_KONAMI_SCC
Definition RomTypes.hh:42
constexpr void fill(ForwardRange &&range, const T &value)
Definition ranges.hh:305
STL namespace.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define UNREACHABLE
constexpr auto xrange(T e)
Definition xrange.hh:132