| 1 |
#include "DalSoRiR2.hh" |
| 2 |
|
| 3 |
#include "MSXCPUInterface.hh" |
| 4 |
#include "serialize.hh" |
| 5 |
|
| 6 |
#include "ranges.hh" |
| 7 |
|
| 8 |
namespace openmsx { |
| 9 |
|
| 10 |
|
| 11 |
static constexpr byte REG_BANK_SEGMENT_MASK = (1 << 5) - 1; |
| 12 |
|
| 13 |
|
| 14 |
static constexpr byte DISABLE = 1 << 7; |
| 15 |
static constexpr byte REG_FRAME_SEGMENT_MASK = (1 << 3) - 1; |
| 16 |
|
| 17 |
|
| 18 |
static constexpr byte ENA_S0 = 1 << 5; |
| 19 |
static constexpr byte ENA_FW = 1 << 4; |
| 20 |
static constexpr byte ENA_C4 = 1 << 1; |
| 21 |
static constexpr byte ENA_C0 = 1 << 0; |
| 22 |
|
| 23 |
DalSoRiR2::DalSoRiR2(DeviceConfig& config) |
| 24 |
: MSXDevice(config) |
| 25 |
, ymf278b(getName(), |
| 26 |
size_t(4096 * 1024), |
| 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 |
|
| 52 |
DalSoRiR2::~DalSoRiR2() |
| 53 |
{ |
| 54 |
setRegCfg(0); |
| 55 |
dipSwitchBDIS.detach(*this); |
| 56 |
} |
| 57 |
|
| 58 |
void 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) const |
| 63 |
{ |
| 64 |
|
| 65 |
|
| 66 |
|
| 67 |
|
| 68 |
static constexpr auto k128 = YMF278::k128; |
| 69 |
|
| 70 |
|
| 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 |
|
| 77 |
if (mode0) [[likely]] { |
| 78 |
|
| 79 |
for (auto i : xrange(16, 32)) { |
| 80 |
memPtrs[i] = subspan<k128>(ram, i * k128); |
| 81 |
} |
| 82 |
} else { |
| 83 |
|
| 84 |
|
| 85 |
|
| 86 |
|
| 87 |
|
| 88 |
for (auto i : xrange(16, 32)) { |
| 89 |
memPtrs[i] = YMF278::Block128{}; |
| 90 |
} |
| 91 |
} |
| 92 |
} |
| 93 |
|
| 94 |
void DalSoRiR2::powerUp(EmuTime time) |
| 95 |
{ |
| 96 |
ymf278b.powerUp(time); |
| 97 |
reset(time); |
| 98 |
} |
| 99 |
|
| 100 |
void DalSoRiR2::reset(EmuTime time) |
| 101 |
{ |
| 102 |
ymf278b.reset(time); |
| 103 |
flash.reset(); |
| 104 |
|
| 105 |
ranges::iota(regBank, byte(0)); |
| 106 |
ranges::iota(regFrame, byte(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 |
|
| 113 |
void 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 { |
| 119 |
getCPUInterface().unregister_IO_InOut_range(base, 4, this); |
| 120 |
} |
| 121 |
}; |
| 122 |
if ((value ^ regCfg) & ENA_C0) { |
| 123 |
registerHelper((value & ENA_C0) != 0, 0xC0); |
| 124 |
} |
| 125 |
if ((value ^ regCfg) & ENA_C4) { |
| 126 |
registerHelper((value & ENA_C4) != 0, 0xC4); |
| 127 |
} |
| 128 |
|
| 129 |
if ((value ^ regCfg) & ENA_FW) { |
| 130 |
invalidateDeviceRWCache(); |
| 131 |
} |
| 132 |
|
| 133 |
regCfg = value; |
| 134 |
|
| 135 |
ymf278b.setupMemoryPointers(); |
| 136 |
} |
| 137 |
|
| 138 |
byte DalSoRiR2::readIO(uint16_t port, EmuTime time) |
| 139 |
{ |
| 140 |
return ymf278b.readIO(port, time); |
| 141 |
} |
| 142 |
|
| 143 |
byte DalSoRiR2::peekIO(uint16_t port, EmuTime time) const |
| 144 |
{ |
| 145 |
return ymf278b.peekIO(port, time); |
| 146 |
} |
| 147 |
|
| 148 |
void DalSoRiR2::writeIO(uint16_t port, byte value, EmuTime time) |
| 149 |
{ |
| 150 |
ymf278b.writeIO(port, value, time); |
| 151 |
} |
| 152 |
|
| 153 |
std::optional<size_t> DalSoRiR2::getSramAddr(uint16_t addr) const |
| 154 |
{ |
| 155 |
auto get = [&](byte frame) -> size_t { |
| 156 |
return (addr & 0x0FFF) + (frame & REG_FRAME_SEGMENT_MASK) * 0x1000; |
| 157 |
}; |
| 158 |
|
| 159 |
if ((0x3000 <= addr) && (addr < 0x4000)) { |
| 160 |
|
| 161 |
return get(0); |
| 162 |
} else if ((0x7000 <= addr) && (addr < 0x8000) && ((regFrame[0] & DISABLE) == 0)) { |
| 163 |
|
| 164 |
return get(regFrame[0]); |
| 165 |
} else if ((0xB000 <= addr) && (addr < 0xC000) && ((regFrame[1] & DISABLE) == 0)) { |
| 166 |
|
| 167 |
return get(regFrame[1]); |
| 168 |
} else { |
| 169 |
return {}; |
| 170 |
} |
| 171 |
} |
| 172 |
|
| 173 |
unsigned DalSoRiR2::getFlashAddr(uint16_t 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 |
|
| 181 |
byte DalSoRiR2::readMem(uint16_t addr, EmuTime time) |
| 182 |
{ |
| 183 |
if (auto r = getSramAddr(addr)) { |
| 184 |
return sram[*r]; |
| 185 |
} else if (!biosDisable) { |
| 186 |
return flash.read(getFlashAddr(addr), time); |
| 187 |
} |
| 188 |
return 0xFF; |
| 189 |
} |
| 190 |
|
| 191 |
byte DalSoRiR2::peekMem(uint16_t addr, EmuTime time) const |
| 192 |
{ |
| 193 |
if (auto r = getSramAddr(addr)) { |
| 194 |
return sram[*r]; |
| 195 |
} else if (!biosDisable) { |
| 196 |
return flash.peek(getFlashAddr(addr), time); |
| 197 |
} |
| 198 |
return 0xFF; |
| 199 |
} |
| 200 |
|
| 201 |
const byte* DalSoRiR2::getReadCacheLine(uint16_t start) const |
| 202 |
{ |
| 203 |
if (auto r = getSramAddr(start)) { |
| 204 |
return &sram[*r]; |
| 205 |
} else if (!biosDisable) { |
| 206 |
return flash.getReadCacheLine(getFlashAddr(start)); |
| 207 |
} |
| 208 |
return unmappedRead.data(); |
| 209 |
} |
| 210 |
|
| 211 |
void DalSoRiR2::writeMem(uint16_t addr, byte value, EmuTime time) |
| 212 |
{ |
| 213 |
if (auto r = getSramAddr(addr)) { |
| 214 |
sram[*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; |
| 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, time); |
| 227 |
} |
| 228 |
} |
| 229 |
|
| 230 |
byte* DalSoRiR2::getWriteCacheLine(uint16_t start) |
| 231 |
{ |
| 232 |
if (auto r = getSramAddr(start)) { |
| 233 |
return &sram[*r]; |
| 234 |
} else { |
| 235 |
|
| 236 |
return nullptr; |
| 237 |
} |
| 238 |
} |
| 239 |
|
| 240 |
void DalSoRiR2::update(const Setting& setting) noexcept |
| 241 |
{ |
| 242 |
assert(&setting == &dipSwitchBDIS); (void)setting; |
| 243 |
auto newBiosDisable = dipSwitchBDIS.getBoolean(); |
| 244 |
if (biosDisable != newBiosDisable) { |
| 245 |
biosDisable = newBiosDisable; |
| 246 |
invalidateDeviceRWCache(); |
| 247 |
} |
| 248 |
} |
| 249 |
|
| 250 |
|
| 251 |
template<typename Archive> |
| 252 |
void DalSoRiR2::serialize(Archive& ar, unsigned ) |
| 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); |
| 263 |
} |
| 264 |
|
| 265 |
} |
| 266 |
INSTANTIATE_SERIALIZE_METHODS(DalSoRiR2); |
| 267 |
REGISTER_MSXDEVICE(DalSoRiR2, "DalSoRiR2"); |
| 268 |
|
| 269 |
} |