openMSX
DalSoRiR2.cc
Go to the documentation of this file.
1#include "DalSoRiR2.hh"
2
3#include "MSXCPUInterface.hh"
4#include "serialize.hh"
5
6#include "ranges.hh"
7
8namespace openmsx {
9
10// bit(s) of the memory bank selection registers regBank[n]
11static constexpr byte REG_BANK_SEGMENT_MASK = (1 << 5) - 1; // 5-bits
12
13// bit(s) of the frame selection registers regFrame[n]
14static constexpr byte DISABLE = 1 << 7;
15static constexpr byte REG_FRAME_SEGMENT_MASK = (1 << 3) - 1; // 3-bits
16
17// bits of the configuration register regCfg
18static constexpr byte ENA_S0 = 1 << 5;
19static constexpr byte ENA_FW = 1 << 4;
20static constexpr byte ENA_C4 = 1 << 1;
21static constexpr byte ENA_C0 = 1 << 0;
22
24 : MSXDevice(config)
25 , ymf278b(getName(),
26 size_t(4096 * 1024), // 4MB RAM
27 config,
28 [this](bool mode0, std::span<const uint8_t> rom, std::span<const uint8_t> ram,
29 std::span<YMF278::Block128, 32> memPtrs) {
30 setupMemPtrs(mode0, rom, ram, memPtrs);
31 }, getCurrentTime())
32 , sram(config, getName() + " RAM", "DalSoRi R2 RAM", 0x8000)
33 , flash(getName() + " flash", AmdFlashChip::SST39SF010, {}, config, "flash")
34 , dipSwitchBDIS(getCommandController(),
35 getName() + " DIP switch BDIS",
36 "Controls the BDIS DIP switch position. ON = Disable BIOS (flash memory access)", false)
37 , dipSwitchMCFG(getCommandController(),
38 getName() + " DIP switch MCFG",
39 "Controls the MCFG DIP switch position. ON = Enable sample RAM instead of sample ROM on reset.", false)
40 , dipSwitchIO_C0(getCommandController(),
41 getName() + " DIP switch IO/C0",
42 "Controls the IO/C0 DIP switch position. ON = Enable I/O addres C0H~C3H on reset. This is the MSX-AUDIO compatible I/O port range.", true)
43 , dipSwitchIO_C4(getCommandController(),
44 getName() + " DIP switch IO/C4",
45 "Controls the IO/C4 DIP switch position. ON = Enable I/O addres C4H~C7H on reset. This is the MoonSound compatible I/O port range", true)
46 , biosDisable(dipSwitchBDIS.getBoolean())
47{
48 dipSwitchBDIS.attach(*this);
49 powerUp(getCurrentTime());
50}
51
53{
54 setRegCfg(0); // unregister any FM I/O ports
55 dipSwitchBDIS.detach(*this);
56}
57
58void DalSoRiR2::setupMemPtrs(
59 bool mode0,
60 std::span<const uint8_t> rom,
61 std::span<const uint8_t> ram,
62 std::span<YMF278::Block128, 32> memPtrs)
63{
64 // /MCS0: connected to either a 2MB ROM or a 2MB RAM chip (dynamically
65 // selectable via software)
66 // /MCS1: connected to a 2MB RAM chip
67 // /MCS2../MCS9: unused
68 static constexpr auto k128 = YMF278::k128;
69
70 // first 2MB, either ROM or RAM, both mode0 and mode1
71 bool enableRam = regCfg & ENA_S0;
72 auto mem0 = enableRam ? std::span<const uint8_t>{ram} : std::span<const uint8_t>{rom};
73 for (auto i : xrange(16)) {
74 memPtrs[i] = subspan<k128>(mem0, i * k128);
75 }
76 // second 2MB
77 if (mode0) [[likely]] {
78 // mode 0: RAM
79 for (auto i : xrange(16, 32)) {
80 memPtrs[i] = subspan<k128>(ram, i * k128);
81 }
82 } else {
83 // mode 1: unmapped (normally shouldn't be used on DalSoRiR2)
84 // Note: in this mode accessing region [0x00'0000, 0x10'0000) activates
85 // both /MCS0 and /MCS1, thus two chips (ROM+RAM or RAM+RAM) get activated
86 // simultaneously. That might cause problems on the real hardware. Here we
87 // emulate it by ignoring the problem, as-if only /MCS0 was active.
88 for (auto i : xrange(16, 32)) {
89 memPtrs[i] = YMF278::Block128{};
90 }
91 }
92}
93
94void DalSoRiR2::powerUp(EmuTime::param time)
95{
96 ymf278b.powerUp(time);
97 reset(time);
98}
99
100void DalSoRiR2::reset(EmuTime::param time)
101{
102 ymf278b.reset(time);
103 flash.reset();
104
105 ranges::iota(regBank, 0);
106 ranges::iota(regFrame, 0);
107
108 setRegCfg((dipSwitchMCFG. getBoolean() ? ENA_S0 : 0) |
109 (dipSwitchIO_C0.getBoolean() ? ENA_C0 : 0) |
110 (dipSwitchIO_C4.getBoolean() ? ENA_C4 : 0));
111}
112
113void DalSoRiR2::setRegCfg(byte value)
114{
115 auto registerHelper = [this](bool enable, byte base) {
116 if (enable) {
117 getCPUInterface(). register_IO_InOut_range(base, 4, this);
118 } else {
120 }
121 };
122 if ((value ^ regCfg) & ENA_C0) { // enable C0 changed
123 registerHelper((value & ENA_C0) != 0, 0xC0);
124 }
125 if ((value ^ regCfg) & ENA_C4) { // enable C4 changed
126 registerHelper((value & ENA_C4) != 0, 0xC4);
127 }
128
129 if ((value ^ regCfg) & ENA_FW) {
131 }
132
133 regCfg = value;
134
135 ymf278b.setupMemoryPointers(); // ENA_S0 might have changed
136}
137
138byte DalSoRiR2::readIO(word port, EmuTime::param time)
139{
140 return ymf278b.readIO(port, time);
141}
142
143byte DalSoRiR2::peekIO(word port, EmuTime::param time) const
144{
145 return ymf278b.peekIO(port, time);
146}
147
148void DalSoRiR2::writeIO(word port, byte value, EmuTime::param time)
149{
150 ymf278b.writeIO(port, value, time);
151}
152
153byte* DalSoRiR2::getSramAddr(word addr)
154{
155 auto get = [&](byte frame) {
156 return &sram[(addr & 0x0FFF) + (frame & REG_FRAME_SEGMENT_MASK) * 0x1000];
157 };
158
159 if ((0x3000 <= addr) && (addr < 0x4000)) {
160 // SRAM frame 0
161 return get(0);
162 } else if ((0x7000 <= addr) && (addr < 0x8000) && ((regFrame[0] & DISABLE) == 0)) {
163 // SRAM frame 1
164 return get(regFrame[0]);
165 } else if ((0xB000 <= addr) && (addr < 0xC000) && ((regFrame[1] & DISABLE) == 0)) {
166 // SRAM frame 2
167 return get(regFrame[1]);
168 } else {
169 return nullptr;
170 }
171}
172
173unsigned DalSoRiR2::getFlashAddr(word addr) const
174{
175 auto page = addr >> 14;
176 auto bank = regBank[page] & REG_BANK_SEGMENT_MASK;
177 auto offset = addr & 0x3FFF;
178 return 0x4000 * bank + offset;
179}
180
181byte DalSoRiR2::readMem(word addr, EmuTime::param /*time*/)
182{
183 if (const auto* r = getSramAddr(addr)) {
184 return *r;
185 } else if (!biosDisable) {
186 return flash.read(getFlashAddr(addr));
187 }
188 return 0xFF;
189}
190
191byte DalSoRiR2::peekMem(word addr, EmuTime::param /*time*/) const
192{
193 if (const auto* r = const_cast<DalSoRiR2*>(this)->getSramAddr(addr)) {
194 return *r;
195 } else if (!biosDisable) {
196 return flash.peek(getFlashAddr(addr));
197 }
198 return 0xFF;
199}
200
201const byte* DalSoRiR2::getReadCacheLine(word start) const
202{
203 if (const auto* r = const_cast<DalSoRiR2*>(this)->getSramAddr(start)) {
204 return r;
205 } else if (!biosDisable) {
206 return flash.getReadCacheLine(getFlashAddr(start));
207 }
208 return unmappedRead.data();
209}
210
211void DalSoRiR2::writeMem(word addr, byte value, EmuTime::param /*time*/)
212{
213 if (auto* r = getSramAddr(addr)) {
214 *r = value;
215 } else if ((0x6000 <= addr) && (addr < 0x6400)) {
216 auto bank = (addr >> 8) & 3;
217 regBank[bank] = value;
218 invalidateDeviceRWCache(bank * 0x4000, 0x4000);
219 } else if ((0x6500 <= addr) && (addr < 0x6700)) {
220 auto frame = (addr >> 9) & 1; // [0x6500,0x65FF] -> 0, [0x6600,0x66FF] -> 1
221 regFrame[frame] = value;
222 invalidateDeviceRWCache(0x7000 + frame * 0x4000, 0x1000);
223 } else if ((0x6700 <= addr) && (addr < 0x6800)) {
224 setRegCfg(value);
225 } else if (!biosDisable && ((regCfg & ENA_FW) != 0)) {
226 flash.write(getFlashAddr(addr), value);
227 }
228}
229
231{
232 if (auto* r = getSramAddr(start)) {
233 return r;
234 } else {
235 // bank/frame/cfg registers or flash, not cacheable
236 return nullptr;
237 }
238}
239
240void DalSoRiR2::update(const Setting& setting) noexcept
241{
242 assert(&setting == &dipSwitchBDIS);
243 auto newBiosDisable = dipSwitchBDIS.getBoolean();
244 if (biosDisable != newBiosDisable) {
245 biosDisable = newBiosDisable;
246 invalidateDeviceRWCache();
247 }
248}
249
250// version 1: initial version
251template<typename Archive>
252void DalSoRiR2::serialize(Archive& ar, unsigned /*version*/)
253{
254 ar.template serializeBase<MSXDevice>(*this);
255 auto backupRegCfg = regCfg;
256 ar.serialize("ymf278b", ymf278b,
257 "sram", sram,
258 "regBank", regBank,
259 "regFrame", regFrame,
260 "regCfg", backupRegCfg);
261 if constexpr (Archive::IS_LOADER) {
262 setRegCfg(backupRegCfg); // register ports and setupMemoryPointers()
263 }
264 // no need to serialize biosDisable
265}
268
269} // namespace openmsx
BaseSetting * setting
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition MSXDevice.hh:356
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
uint8_t peek(size_t address) const
Definition AmdFlash.cc:186
bool getBoolean() const noexcept
DalSoRiR2(const DeviceConfig &config)
Definition DalSoRiR2.cc:23
void writeMem(word addr, byte value, EmuTime::param time) override
Write a given byte to a given location at a certain time to this device.
Definition DalSoRiR2.cc:211
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 DalSoRiR2.cc:148
byte peekMem(word addr, EmuTime::param time) const override
Read a byte from a given memory location.
Definition DalSoRiR2.cc:191
const byte * getReadCacheLine(word start) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading.
Definition DalSoRiR2.cc:201
byte readMem(word addr, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
Definition DalSoRiR2.cc:181
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
Definition DalSoRiR2.cc:138
~DalSoRiR2() override
Definition DalSoRiR2.cc:52
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
Definition DalSoRiR2.cc:94
void reset(EmuTime::param time) override
This method is called on reset.
Definition DalSoRiR2.cc:100
byte * getWriteCacheLine(word start) override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
Definition DalSoRiR2.cc:230
void serialize(Archive &ar, unsigned version)
Definition DalSoRiR2.cc:252
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
Definition DalSoRiR2.cc:143
void unregister_IO_InOut_range(byte port, unsigned num, MSXDevice *device)
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition MSXDevice.hh:36
static std::array< byte, 0x10000 > unmappedRead
Definition MSXDevice.hh:306
void invalidateDeviceRWCache()
Calls MSXCPUInterface::invalidateXXCache() for the specific (part of) the slot that this device is lo...
Definition MSXDevice.hh:214
MSXCPUInterface & getCPUInterface() const
Definition MSXDevice.cc:133
void detach(Observer< T > &observer)
Definition Subject.hh:60
void setupMemoryPointers()
Definition YMF278B.cc:214
void writeIO(word port, byte value, EmuTime::param time)
Definition YMF278B.cc:138
void powerUp(EmuTime::param time)
Definition YMF278B.cc:51
byte readIO(word port, EmuTime::param time)
Definition YMF278B.cc:69
byte peekIO(word port, EmuTime::param time) const
Definition YMF278B.cc:111
void reset(EmuTime::param time)
Definition YMF278B.cc:57
static constexpr auto k128
Definition YMF278.hh:27
optional_fixed_span< const uint8_t, k128 > Block128
Definition YMF278.hh:28
This file implemented 3 utility functions:
Definition Autofire.cc:11
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
constexpr void iota(ForwardIt first, ForwardIt last, T value)
Definition ranges.hh:322
STL namespace.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
constexpr auto xrange(T e)
Definition xrange.hh:132