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 return config1; // dummy
62 }
63}
64
66 RomType type)
67 : MSXRom(config, std::move(rom_))
68 , scc((type != ROM_RBSC_FLASH_KONAMI_SCC)
69 ? std::make_unique<SCC>(
70 getName() + " SCC", config, getCurrentTime())
71 : nullptr)
72 , psg((type == one_of(ROM_MANBOW2_2, ROM_HAMARAJANIGHT))
73 ? std::make_unique<AY8910>(
74 getName() + " PSG", DummyAY8910Periphery::instance(),
75 config, getCurrentTime())
76 : nullptr)
77 , flash(rom, getSectorInfo(type),
78 type == ROM_RBSC_FLASH_KONAMI_SCC ? 0x01AD : 0x01A4,
79 AmdFlash::Addressing::BITS_11, config)
80 , romBlockDebug(*this, bank, 0x4000, 0x8000, 13)
81{
83
84 if (psg) {
85 getCPUInterface().register_IO_Out(0x10, this);
86 getCPUInterface().register_IO_Out(0x11, this);
87 getCPUInterface().register_IO_In (0x12, this);
88 }
89}
90
92{
93 if (psg) {
96 getCPUInterface().unregister_IO_In (0x12, this);
97 }
98}
99
100void RomManbow2::powerUp(EmuTime::param time)
101{
102 if (scc) {
103 scc->powerUp(time);
104 }
105 reset(time);
106}
107
108void RomManbow2::reset(EmuTime::param time)
109{
110 for (auto i : xrange(4)) {
111 setRom(i, byte(i));
112 }
113
114 sccEnabled = false;
115 if (scc) {
116 scc->reset(time);
117 }
118
119 if (psg) {
120 psgLatch = 0;
121 psg->reset(time);
122 }
123
124 flash.reset();
125}
126
127void RomManbow2::setRom(unsigned region, byte block)
128{
129 assert(region < 4);
130 auto nrBlocks = narrow<unsigned>(flash.size() / 0x2000);
131 bank[region] = block & narrow<byte>(nrBlocks - 1);
132 invalidateDeviceRCache(0x4000 + region * 0x2000, 0x2000);
133}
134
135byte RomManbow2::peekMem(word address, EmuTime::param time) const
136{
137 if (sccEnabled && (0x9800 <= address) && (address < 0xA000)) {
138 return scc->peekMem(narrow_cast<uint8_t>(address & 0xFF), time);
139 } else if ((0x4000 <= address) && (address < 0xC000)) {
140 unsigned page = (address - 0x4000) / 0x2000;
141 unsigned addr = (address & 0x1FFF) + 0x2000 * bank[page];
142 return flash.peek(addr);
143 } else {
144 return 0xFF;
145 }
146}
147
148byte RomManbow2::readMem(word address, EmuTime::param time)
149{
150 if (sccEnabled && (0x9800 <= address) && (address < 0xA000)) {
151 return scc->readMem(narrow_cast<uint8_t>(address & 0xFF), time);
152 } else if ((0x4000 <= address) && (address < 0xC000)) {
153 unsigned page = (address - 0x4000) / 0x2000;
154 unsigned addr = (address & 0x1FFF) + 0x2000 * bank[page];
155 return flash.read(addr);
156 } else {
157 return 0xFF;
158 }
159}
160
161const byte* RomManbow2::getReadCacheLine(word address) const
162{
163 if (sccEnabled && (0x9800 <= address) && (address < 0xA000)) {
164 return nullptr;
165 } else if ((0x4000 <= address) && (address < 0xC000)) {
166 unsigned page = (address - 0x4000) / 0x2000;
167 unsigned addr = (address & 0x1FFF) + 0x2000 * bank[page];
168 return flash.getReadCacheLine(addr);
169 } else {
170 return unmappedRead.data();
171 }
172}
173
174void RomManbow2::writeMem(word address, byte value, EmuTime::param time)
175{
176 if (sccEnabled && (0x9800 <= address) && (address < 0xA000)) {
177 // write to SCC
178 scc->writeMem(narrow_cast<uint8_t>(address & 0xff), value, time);
179 // note: writes to SCC also go to flash
180 // thanks to 'enen' for testing this
181 }
182 if ((0x4000 <= address) && (address < 0xC000)) {
183 unsigned page = (address - 0x4000) / 0x2000;
184 unsigned addr = (address & 0x1FFF) + 0x2000 * bank[page];
185 flash.write(addr, value);
186
187 if (scc && ((address & 0xF800) == 0x9000)) {
188 // SCC enable/disable
189 sccEnabled = ((value & 0x3F) == 0x3F);
190 invalidateDeviceRCache(0x9800, 0x0800);
191 }
192 if ((address & 0x1800) == 0x1000) {
193 // page selection
194 setRom(page, value);
195 }
196 }
197}
198
200{
201 if ((0x4000 <= address) && (address < 0xC000)) {
202 return nullptr;
203 } else {
204 return unmappedWrite.data();
205 }
206}
207
208byte RomManbow2::readIO(word port, EmuTime::param time)
209{
210 assert((port & 0xFF) == 0x12); (void)port;
211 return psg->readRegister(psgLatch, time);
212}
213
214byte RomManbow2::peekIO(word port, EmuTime::param time) const
215{
216 assert((port & 0xFF) == 0x12); (void)port;
217 return psg->peekRegister(psgLatch, time);
218}
219
220void RomManbow2::writeIO(word port, byte value, EmuTime::param time)
221{
222 if ((port & 0xFF) == 0x10) {
223 psgLatch = value & 0x0F;
224 } else {
225 assert((port & 0xFF) == 0x11);
226 psg->writeRegister(psgLatch, value, time);
227 }
228}
229
230
231// version 1: initial version
232// version 2: added optional built-in PSG
233// version 3: made SCC optional (for ROM_RBSC_FLASH_KONAMI_SCC)
234template<typename Archive>
235void RomManbow2::serialize(Archive& ar, unsigned version)
236{
237 // skip MSXRom base class
238 ar.template serializeBase<MSXDevice>(*this);
239
240 if (scc) {
241 ar.serialize("scc", *scc,
242 "sccEnabled", sccEnabled);
243 }
244 if ((ar.versionAtLeast(version, 2)) && psg) {
245 ar.serialize("psg", *psg,
246 "psgLatch", psgLatch);
247 }
248 ar.serialize("flash", flash,
249 "bank", bank);
250}
253
254} // namespace openmsx
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition MSXDevice.hh:354
This class implements the AY-3-8910 sound chip.
Definition AY8910.hh:21
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 register_IO_Out(byte port, MSXDevice *device)
Devices can register their Out ports.
void register_IO_In(byte port, MSXDevice *device)
Devices can register their In ports.
void unregister_IO_In(byte port, MSXDevice *device)
void unregister_IO_Out(byte port, MSXDevice *device)
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:91
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:65
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:9
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