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