19 static constexpr
auto sectorInfo = [] {
21 using Info = AmdFlash::SectorInfo;
22 std::array<Info, 8 + 127> result = {};
23 cstd::fill(result.begin(), result.begin() + 8,
Info{ 8 * 1024, false});
24 cstd::fill(result.begin() + 8, result.end(),
Info{64 * 1024, false});
31 , flash(
"Carnivore2 flash", sectorInfo, 0x207e,
32 AmdFlash::Addressing::BITS_12, config)
33 , ram(config,
getName() +
" ram",
"ram", 2048 * 1024)
36 , scc(
getName() +
" scc", config, getCurrentTime(),
SCC::SCC_Compatible)
37 , ym2413(
getName() +
" ym2413", config)
49 writeSndLVL(0x1b, time);
60 configRegs[0x00] = 0x30;
61 for (
int i : {0x01, 0x02, 0x03}) configRegs[i] = 0;
62 configRegs[0x05] = shadowConfigRegs[0x05] = 0;
64 configRegs[0x06] = shadowConfigRegs[0x06] = 0xF8;
65 configRegs[0x07] = shadowConfigRegs[0x07] = 0x50;
66 configRegs[0x08] = shadowConfigRegs[0x08] = 0x00;
67 configRegs[0x09] = shadowConfigRegs[0x09] = 0x85;
68 configRegs[0x0a] = shadowConfigRegs[0x0a] = 0x03;
69 configRegs[0x0b] = shadowConfigRegs[0x0b] = 0x40;
71 for (
int i : {0x0f, 0x15, 0x1b}) {
72 configRegs[i] = shadowConfigRegs[i] = 0;
75 configRegs[0x1e] = shadowConfigRegs[0x1e] = 0xff;
76 configRegs[0x20] = 0x02;
77 configRegs[0x28] = 0b11'10'01'00;
79 writeCfgEEPR(0, time);
88 ideSelectedDevice = 0;
90 ideDevices[0]->reset(time);
91 ideDevices[1]->reset(time);
106 if (!delayedConfig())
return;
108 if ((!delayedConfig4000() && (address == 0x0000) && (
getCPU().isM1Cycle(address))) ||
109 ( delayedConfig4000() && (address <= 0x4000) && (address < 0x4010))) {
111 for (
auto i :
xrange(0x05, 0x1f)) {
112 configRegs[i] = shadowConfigRegs[i];
117 Carnivore2::SubDevice Carnivore2::getSubDevice(
word address)
const
121 if (slotExpanded()) {
122 byte page = address >> 14;
123 byte selectedSubSlot = (subSlotReg >> (2 * page)) & 0x03;
124 if (subSlotEnabled(selectedSubSlot)) {
125 subSlot = selectedSubSlot;
128 for (
auto i :
xrange(4)) {
129 if (subSlotEnabled(i)) {
136 if (subSlot == (configRegs[0x28] & 0b00'00'00'11) >> 0) {
137 return SubDevice::MultiMapper;
138 }
else if (subSlot == (configRegs[0x28] & 0b00'00'11'00) >> 2) {
139 return SubDevice::IDE;
140 }
else if (subSlot == (configRegs[0x28] & 0b00'11'00'00) >> 4) {
141 return SubDevice::MemoryMapper;
142 }
else if (subSlot == (configRegs[0x28] & 0b11'00'00'00) >> 6) {
143 return SubDevice::FmPac;
145 return SubDevice::Nothing;
151 if (slotExpanded() && (address == 0xffff)) {
152 return subSlotReg ^ 0xff;
154 switch (getSubDevice(address)) {
155 case SubDevice::MultiMapper:
return readMultiMapperSlot(address, time);
156 case SubDevice::IDE:
return readIDESlot(address, time);
157 case SubDevice::MemoryMapper:
return readMemoryMapperSlot(address);
158 case SubDevice::FmPac:
return readFmPacSlot(address);
159 default:
return 0xff;
165 if (slotExpanded() && (address == 0xffff)) {
166 return subSlotReg ^ 0xff;
168 switch (getSubDevice(address)) {
169 case SubDevice::MultiMapper:
return peekMultiMapperSlot(address, time);
170 case SubDevice::IDE:
return peekIDESlot(address, time);
171 case SubDevice::MemoryMapper:
return peekMemoryMapperSlot(address);
172 case SubDevice::FmPac:
return peekFmPacSlot(address);
173 default:
return 0xff;
179 if (slotExpanded() && (address == 0xffff)) {
184 switch (getSubDevice(address)) {
185 case SubDevice::MultiMapper:
186 writeMultiMapperSlot(address, value, time);
189 writeIDESlot(address, value, time);
191 case SubDevice::MemoryMapper:
192 writeMemoryMapperSlot(address, value);
194 case SubDevice::FmPac:
195 writeFmPacSlot(address, value, time);
203 unsigned Carnivore2::getDirectFlashAddr()
const
205 return (configRegs[0x01] << 0) |
206 (configRegs[0x02] << 8) |
207 (configRegs[0x03] << 16);
210 byte Carnivore2::peekConfigRegister(
word address, EmuTime::param time)
const
213 if ((0x05 <= address) && (address <= 0x1e)) {
216 return shadowConfigRegs[address];
219 case 0x04:
return flash.
peek(getDirectFlashAddr());
220 case 0x1f:
return configRegs[0x00];
221 case 0x23:
return configRegs[address] |
223 case 0x2C:
return '2';
224 case 0x2D:
return '3';
225 case 0x2E:
return '0';
226 default:
return configRegs[address];
231 byte Carnivore2::readConfigRegister(
word address, EmuTime::param time)
234 if (address == 0x04) {
235 return flash.
read(getDirectFlashAddr());
237 return peekConfigRegister(address, time);
241 static constexpr
float volumeLevel(
byte volume)
243 constexpr
byte tab[8] = {5, 6, 7, 8, 10, 12, 14, 16};
244 return tab[volume & 7] / 16.0f;
247 void Carnivore2::writeSndLVL(
byte value, EmuTime::param time)
249 configRegs[0x22] = value;
254 void Carnivore2::writeCfgEEPR(
byte value, EmuTime::param time)
256 configRegs[0x23] = value & 0x0e;
263 [[nodiscard]]
static bool bitPairsUnique(uint8_t
x)
266 for (
int i = 0; i < 4; ++i) {
267 seen |= 1 << (
x & 3);
270 return seen == 0b1111;
273 void Carnivore2::writeConfigRegister(
word address,
byte value, EmuTime::param time)
276 if ((0x05 <= address) && (address <= 0x1e)) {
278 if (address == 0x05) value &= 0x7f;
279 if ((address == 0x1e) && ((value & 0x8f) == 0x0f))
return;
281 shadowConfigRegs[address] = value;
282 if (!delayedConfig()) configRegs[address] = value;
285 case 0x03: configRegs[address] = value & 0x7f;
break;
286 case 0x04: flash.
write(getDirectFlashAddr(), value);
break;
287 case 0x1f: configRegs[0x00] = value;
break;
288 case 0x20: configRegs[address] = value & 0x07;
break;
289 case 0x22: writeSndLVL(value, time);
break;
290 case 0x23: writeCfgEEPR(value, time);
break;
293 if (!bitPairsUnique(value)) {
295 "Illegal value of ", value,
296 "written to SLM_cfg register");
299 default: configRegs[address] = value;
break;
304 bool Carnivore2::isConfigReg(
word address)
const
306 if (configRegs[0x00] & 0x80)
return false;
307 unsigned base = ((configRegs[0x00] & 0x60) << 9) | 0xF80;
308 return (base <= address) && (address < (base + 0x40));
311 std::pair<unsigned, byte> Carnivore2::decodeMultiMapper(
word address)
const
314 for (
auto i :
xrange(4)) {
315 const byte* base = configRegs + (i * 6) + 6;
317 if (mult & 8)
continue;
319 byte sizeCode = mult & 7;
320 if (sizeCode < 3)
continue;
323 bool mirroringDisabled = mult & 0x40;
324 static constexpr
byte checkMasks[2][8] = {
325 { 0x00, 0x00, 0x00, 0x30, 0x60, 0xc0, 0x80, 0x00 },
326 { 0x00, 0x00, 0x00, 0xf0, 0xe0, 0xc0, 0x80, 0x00 },
328 byte checkMask = checkMasks[mirroringDisabled][sizeCode];
329 if (((address >> 8) & checkMask) != (base[5] & checkMask))
continue;
332 byte bank = base[2] & base[4];
333 unsigned size = 512 << sizeCode;
334 unsigned addr = (bank *
size) | (address & (
size - 1));
335 addr += configRegs[0x05] * 0x10000;
339 return {unsigned(-1),
byte(-1)};
342 bool Carnivore2::sccAccess(
word address)
const
344 if (!sccEnabled())
return false;
345 if (sccMode & 0x20) {
347 return (0xb800 <= address) && (address < 0xc000) &&
348 ((sccBank[3] & 0x80) == 0x80);
351 return (0x9800 <= address) && (address < 0xa000) &&
352 ((sccBank[2] & 0x3f) == 0x3f);
356 byte Carnivore2::readMultiMapperSlot(
word address, EmuTime::param time)
358 if (isConfigReg(address)) {
359 return readConfigRegister(address, time);
361 if (sccAccess(address)) {
362 return scc.
readMem(address & 0xff, time);
365 auto [addr, mult] = decodeMultiMapper(address);
366 if (addr ==
unsigned(-1))
return 0xff;
369 return ram[addr & 0x1fffff];
371 return flash.
read(addr);
375 byte Carnivore2::peekMultiMapperSlot(
word address, EmuTime::param time)
const
377 if (isConfigReg(address)) {
378 return peekConfigRegister(address, time);
381 auto [addr, mult] = decodeMultiMapper(address);
382 if (addr ==
unsigned(-1))
return 0xff;
385 return ram[addr & 0x1fffff];
387 return flash.
peek(addr);
391 void Carnivore2::writeMultiMapperSlot(
word address,
byte value, EmuTime::param time)
393 if (isConfigReg(address)) {
395 return writeConfigRegister(address, value, time);
399 for (
auto i :
xrange(4)) {
400 byte* base = configRegs + (i * 6) + 6;
405 if (((address >> 8) &
mask) == (addr &
mask)) {
407 configRegs[(i * 6) + 6 + 2] = value;
408 shadowConfigRegs[(i * 6) + 6 + 2] = value;
413 auto [addr, mult] = decodeMultiMapper(address);
414 if ((addr !=
unsigned(-1)) && (mult & 0x10)) {
416 ram[addr & 0x1fffff] = value;
418 flash.
write(addr, value);
422 if (sccEnabled() && ((address | 1) == 0xbfff)) {
427 if (((sccMode & 0x10) == 0x00) &&
428 ((address & 0x1800) == 0x1000)) {
429 byte region = (address >> 13) - 2;
430 sccBank[region] = value;
431 }
else if (sccAccess(address)) {
432 scc.
writeMem(address & 0xff, value, time);
436 byte Carnivore2::readIDESlot(
word address, EmuTime::param time)
439 if (ideRegsEnabled() && ((address & 0xfe00) == 0x7c00)) {
441 switch (address & 1) {
443 auto tmp = ideReadData(time);
451 if (ideRegsEnabled() && ((address & 0xff00) == 0x7e00)) {
453 return ideReadReg(address & 0xf, time);
455 if ((0x4000 <= address) && (address < 0x8000)) {
457 unsigned addr = (address & 0x3fff) + (ideBank() * 0x4000) + 0x10000;
458 if (readBIOSfromRAM()) {
461 return flash.
read(addr);
467 byte Carnivore2::peekIDESlot(
word address, EmuTime::param )
const
469 if (ideRegsEnabled() && ((address & 0xfe00) == 0x7c00)) {
473 if (ideRegsEnabled() && ((address & 0xff00) == 0x7e00)) {
477 if ((0x4000 <= address) && (address < 0x8000)) {
479 unsigned addr = (address & 0x3fff) + (ideBank() * 0x4000) + 0x10000;
480 if (readBIOSfromRAM()) {
483 return flash.
peek(addr);
489 void Carnivore2::writeIDESlot(
word address,
byte value, EmuTime::param time)
492 if (address == 0x4104) {
493 ideControlReg = value;
495 }
else if (ideRegsEnabled() && ((address & 0xfe00) == 0x7c00)) {
497 switch (address & 1) {
502 word tmp = (value << 8) | ideWrite;
503 ideWriteData(tmp, time);
508 }
else if (ideRegsEnabled() && ((address & 0xff00) == 0x7e00)) {
510 ideWriteReg(address & 0xf, value, time);
514 word Carnivore2::ideReadData(EmuTime::param time)
516 return ideDevices[ideSelectedDevice]->readData(time);
519 void Carnivore2::ideWriteData(
word value, EmuTime::param time)
521 ideDevices[ideSelectedDevice]->writeData(value, time);
524 byte Carnivore2::ideReadReg(
byte reg, EmuTime::param time)
526 if (reg == 14) reg = 7;
536 return ideReadData(time) & 0xff;
538 auto result = ideDevices[ideSelectedDevice]->readReg(reg, time);
540 result = (result & 0xef) | (ideSelectedDevice ? 0x10 : 0x00);
547 void Carnivore2::ideWriteReg(
byte reg,
byte value, EmuTime::param time)
550 if ((reg == 14) && !(value & 0x04)) {
552 ideSoftReset =
false;
557 ideWriteData((value << 8) | value, time);
559 if ((reg == 14) && (value & 0x04)) {
562 ideDevices[0]->reset(time);
563 ideDevices[1]->reset(time);
566 ideSelectedDevice = (value & 0x10) ? 1 : 0;
568 ideDevices[ideSelectedDevice]->writeReg(reg, value, time);
574 bool Carnivore2::isMemmapControl(
word address)
const
576 return (port3C & 0x80) &&
577 (( (port3C & 0x08) && ((address & 0xc000) == 0x4000)) ||
578 (!(port3C & 0x08) && ((address & 0xc000) == 0x8000)));
581 unsigned Carnivore2::getMemoryMapperAddress(
word address)
const
583 return (address & 0x3fff) +
584 0x4000 * memMapRegs[address >> 14] +
588 bool Carnivore2::isMemoryMapperWriteProtected(
word address)
const
590 byte page = address >> 14;
591 return (port3C & (1 << page)) != 0;
594 byte Carnivore2::peekMemoryMapperSlot(
word address)
const
596 if (isMemmapControl(address)) {
597 switch (address & 0xff) {
600 case 0xfc:
case 0xfd:
case 0xfe:
case 0xff:
601 return memMapRegs[address & 0x03];
604 return ram[getMemoryMapperAddress(address)];
607 byte Carnivore2::readMemoryMapperSlot(
word address)
609 return peekMemoryMapperSlot(address);
612 void Carnivore2::writeMemoryMapperSlot(
word address,
byte value)
614 if (isMemmapControl(address)) {
615 switch (address & 0xff) {
617 value |= (value & 0x02) << 6;
620 case 0xfc:
case 0xfd:
case 0xfe:
case 0xff:
621 memMapRegs[address & 0x03] = value & 0x3f;
625 if (!isMemoryMapperWriteProtected(address)) {
626 ram[getMemoryMapperAddress(address)] = value;
630 byte Carnivore2::readFmPacSlot(
word address)
632 if (address == 0x7ff6) {
634 }
else if (address == 0x7ff7) {
636 }
else if ((0x4000 <= address) && (address < 0x8000)) {
637 if (fmPacSramEnabled()) {
638 if (address < 0x5ffe) {
639 return ram[(address & 0x1fff) | 0xfe000];
640 }
else if (address == 0x5ffe) {
642 }
else if (address == 0x5fff) {
648 unsigned addr = (address & 0x3fff) + (0x4000 * fmPacBank) + 0x30000;
649 if (readBIOSfromRAM()) {
652 return flash.
read(addr);
659 byte Carnivore2::peekFmPacSlot(
word address)
const
661 if (address == 0x7ff6) {
663 }
else if (address == 0x7ff7) {
665 }
else if ((0x4000 <= address) && (address < 0x8000)) {
666 if (fmPacSramEnabled()) {
667 if (address < 0x5ffe) {
668 return ram[(address & 0x1fff) | 0xfe000];
669 }
else if (address == 0x5ffe) {
671 }
else if (address == 0x5fff) {
677 unsigned addr = (address & 0x3fff) + (0x4000 * fmPacBank) + 0x30000;
678 if (readBIOSfromRAM()) {
681 return flash.
peek(addr);
688 void Carnivore2::writeFmPacSlot(
word address,
byte value, EmuTime::param time)
690 if ((0x4000 <= address) && (address < 0x5ffe)) {
691 if (fmPacSramEnabled()) {
692 ram[(address & 0x1fff) | 0xfe000] = value;
694 }
else if (address == 0x5ffe) {
696 }
else if (address == 0x5fff) {
698 }
else if (address ==
one_of(0x7ff4, 0x7ff5)) {
699 ym2413.
writePort(address & 1, value, time);
700 }
else if (address == 0x7ff6) {
701 fmPacEnable = value & 0x11;
702 }
else if (address == 0x7ff7) {
703 fmPacBank = value & 0x03;
709 return peekIO(port, time);
715 if (memMapReadEnabled() && ((port & 0xfc) == 0xfc)) {
724 if (((port & 0xfe) == 0x7c) &&
725 (fmPacPortEnabled1() || fmPacPortEnabled2())) {
728 }
else if (((port & 0xff) == 0x3c) && writePort3cEnabled()) {
730 port3C = (port3C & 0x7F) | (value & 0x80);
732 }
else if ((port & 0xfc) == 0xfc) {
734 memMapRegs[port & 0x03] = value & 0x3f;
741 return memMapRegs[page];
746 template<
typename Archive>
749 ar.template serializeBase<MSXDevice>(*
this);
750 ar.serialize(
"flash", flash,
753 "configRegs", configRegs,
754 "shadowConfigRegs", shadowConfigRegs,
755 "subSlotReg", subSlotReg,
762 ar.serializePolymorphic(
"master", *ideDevices[0]);
763 ar.serializePolymorphic(
"slave", *ideDevices[1]);
764 ar.serialize(
"ideSoftReset", ideSoftReset,
765 "ideSelectedDevice", ideSelectedDevice,
766 "ideControlReg", ideControlReg,
768 "ideWrite", ideWrite,
770 "memMapRegs", memMapRegs,
773 "fmPacEnable", fmPacEnable,
774 "fmPacBank", fmPacBank,
775 "fmPac5ffe", fmPac5ffe,
776 "fmPac5fff", fmPac5fff);
778 if constexpr (Archive::IS_LOADER) {
780 writeSndLVL (configRegs[0x22], time);
781 writeCfgEEPR(configRegs[0x23], time);
void write(unsigned address, byte value)
byte peek(unsigned address) const
byte read(unsigned address) const
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
void reset(EmuTime::param time) override
This method is called on reset.
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.
void serialize(Archive &ar, unsigned version)
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
void globalRead(word address, EmuTime::param time) override
Global reads.
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
Carnivore2(const DeviceConfig &config)
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.
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
byte getSelectedSegment(byte page) const override
void printWarning(std::string_view message)
const XMLElement * findChild(std::string_view name) const
void write_CS(bool value, EmuTime::param time)
void write_CLK(bool value, EmuTime::param time)
bool read_DO(EmuTime::param time) const
void write_DI(bool value, EmuTime::param time)
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
CliComm & getCliComm() const
void invalidateDeviceRWCache()
Calls MSXCPUInterface::invalidateXXCache() for the specific (part of) the slot that this device is lo...
EmuTime::param getCurrentTime() const
void writeMem(byte address, byte value, EmuTime::param time)
void setChipMode(ChipMode newMode)
void powerUp(EmuTime::param time)
byte readMem(byte address, EmuTime::param time)
void reset(EmuTime::param time)
void setSoftwareVolume(float volume, EmuTime::param time)
Change the 'software volume' of this sound device.
void writePort(bool port, byte value, EmuTime::param time)
void reset(EmuTime::param time)
constexpr void fill(ForwardIt first, ForwardIt last, const T &value)
std::unique_ptr< IDEDevice > create(const DeviceConfig &config)
std::string getName(KeyCode keyCode)
Translate key code to key name.
This file implemented 3 utility functions:
uint8_t byte
8 bit unsigned integer
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
AmdFlash::SectorInfo Info
uint16_t word
16 bit unsigned integer
constexpr KeyMatrixPosition x
Keyboard bindings.
constexpr nibble mask[4][13]
constexpr void iota(ForwardIt first, ForwardIt last, T value)
size_t size(std::string_view utf8)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
constexpr auto xrange(T e)