openMSX
Carnivore2.cc
Go to the documentation of this file.
1 #include "Carnivore2.hh"
2 #include "IDEDevice.hh"
3 #include "IDEDeviceFactory.hh"
4 #include "CliComm.hh"
5 #include "MSXCPU.hh"
6 #include "cstd.hh"
7 #include "one_of.hh"
8 #include "ranges.hh"
9 #include "xrange.hh"
10 #include <array>
11 
12 // TODO (besides what's in the code below):
13 // - FM-PAC mono/stereo setting (bit 7)
14 // - possibly the PPI volume setting
15 // - slave slot support
16 
17 namespace openmsx {
18 
19 static constexpr auto sectorInfo = [] {
20  // 8 * 8kB, followed by 127 * 64kB
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});
25  return result;
26 }();
27 
29  : MSXDevice(config)
30  , MSXMapperIOClient(getMotherBoard())
31  , flash("Carnivore2 flash", sectorInfo, 0x207e,
32  AmdFlash::Addressing::BITS_12, config)
33  , ram(config, getName() + " ram", "ram", 2048 * 1024)
34  , eeprom(getName() + " eeprom",
35  DeviceConfig(config, config.getChild("eeprom")))
36  , scc(getName() + " scc", config, getCurrentTime(), SCC::SCC_Compatible)
37  , ym2413(getName() + " ym2413", config)
38 {
39  ideDevices[0] = IDEDeviceFactory::create(
40  DeviceConfig(config, config.findChild("master")));
41  ideDevices[1] = IDEDeviceFactory::create(
42  DeviceConfig(config, config.findChild("slave")));
43 }
44 
45 Carnivore2::~Carnivore2() = default;
46 
47 void Carnivore2::powerUp(EmuTime::param time)
48 {
49  writeSndLVL(0x1b, time);
50  scc.powerUp(time);
51  reset(time);
52 }
53 
54 void Carnivore2::reset(EmuTime::param time)
55 {
56  subSlotReg = 0;
57  port3C = 0;
58 
59  // config regs
60  configRegs[0x00] = 0x30; // CardMDR
61  for (int i : {0x01, 0x02, 0x03}) configRegs[i] = 0; // AddrM<i>
62  configRegs[0x05] = shadowConfigRegs[0x05] = 0; // AddrFR
63 
64  configRegs[0x06] = shadowConfigRegs[0x06] = 0xF8; // R1Mask
65  configRegs[0x07] = shadowConfigRegs[0x07] = 0x50; // R1Addr
66  configRegs[0x08] = shadowConfigRegs[0x08] = 0x00; // R1Reg
67  configRegs[0x09] = shadowConfigRegs[0x09] = 0x85; // R1Mult
68  configRegs[0x0a] = shadowConfigRegs[0x0a] = 0x03; // B1MaskR
69  configRegs[0x0b] = shadowConfigRegs[0x0b] = 0x40; // B1AdrD
70 
71  for (int i : {0x0f, 0x15, 0x1b}) {
72  configRegs[i] = shadowConfigRegs[i] = 0; // R<i>Mult
73  }
74 
75  configRegs[0x1e] = shadowConfigRegs[0x1e] = 0xff;
76  configRegs[0x20] = 0x02;
77  configRegs[0x28] = 0b11'10'01'00; // SLM_cfg
78 
79  writeCfgEEPR(0, time);
80 
81  // multi-mapper
82  scc.reset(time);
83  sccMode = 0;
84  ranges::iota(sccBank, 0);
85 
86  // ide
87  ideControlReg = 0;
88  ideSelectedDevice = 0;
89  ideSoftReset = false;
90  ideDevices[0]->reset(time);
91  ideDevices[1]->reset(time);
92 
93  // memory mapper
94  ranges::iota(memMapRegs, 0); // Note: different from how BIOS initializes these registers
95 
96  // fm-pac
97  ym2413.reset(time);
98  fmPacEnable = 0x10;
99  fmPacBank = 0;
100  fmPac5ffe = 0;
101  fmPac5fff = 0;
102 }
103 
104 void Carnivore2::globalRead(word address, EmuTime::param /*time*/)
105 {
106  if (!delayedConfig()) return;
107 
108  if ((!delayedConfig4000() && (address == 0x0000) && (getCPU().isM1Cycle(address))) ||
109  ( delayedConfig4000() && (address <= 0x4000) && (address < 0x4010))) {
110  // activate delayed configuration
111  for (auto i : xrange(0x05, 0x1f)) {
112  configRegs[i] = shadowConfigRegs[i];
113  }
114  }
115 }
116 
117 Carnivore2::SubDevice Carnivore2::getSubDevice(word address) const
118 {
119  byte subSlot = 0xff;
120 
121  if (slotExpanded()) {
122  byte page = address >> 14;
123  byte selectedSubSlot = (subSlotReg >> (2 * page)) & 0x03;
124  if (subSlotEnabled(selectedSubSlot)) {
125  subSlot = selectedSubSlot;
126  }
127  } else {
128  for (auto i : xrange(4)) {
129  if (subSlotEnabled(i)) {
130  subSlot = i;
131  break;
132  }
133  }
134  }
135 
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;
144  } else {
145  return SubDevice::Nothing;
146  }
147 }
148 
149 byte Carnivore2::readMem(word address, EmuTime::param time)
150 {
151  if (slotExpanded() && (address == 0xffff)) {
152  return subSlotReg ^ 0xff;
153  }
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;
160  }
161 }
162 
163 byte Carnivore2::peekMem(word address, EmuTime::param time) const
164 {
165  if (slotExpanded() && (address == 0xffff)) {
166  return subSlotReg ^ 0xff;
167  }
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;
174  }
175 }
176 
177 void Carnivore2::writeMem(word address, byte value, EmuTime::param time)
178 {
179  if (slotExpanded() && (address == 0xffff)) {
180  subSlotReg = value;
181  // this does not block the writes below
182  }
183 
184  switch (getSubDevice(address)) {
185  case SubDevice::MultiMapper:
186  writeMultiMapperSlot(address, value, time);
187  break;
188  case SubDevice::IDE:
189  writeIDESlot(address, value, time);
190  break;
191  case SubDevice::MemoryMapper:
192  writeMemoryMapperSlot(address, value);
193  break;
194  case SubDevice::FmPac:
195  writeFmPacSlot(address, value, time);
196  break;
197  default:
198  // unmapped, do nothing
199  break;
200  }
201 }
202 
203 unsigned Carnivore2::getDirectFlashAddr() const
204 {
205  return (configRegs[0x01] << 0) |
206  (configRegs[0x02] << 8) |
207  (configRegs[0x03] << 16); // already masked to 7 bits
208 }
209 
210 byte Carnivore2::peekConfigRegister(word address, EmuTime::param time) const
211 {
212  address &= 0x3f;
213  if ((0x05 <= address) && (address <= 0x1e)) {
214  // only these registers have a shadow counterpart,
215  // reads happen from the shadowed version
216  return shadowConfigRegs[address];
217  } else {
218  switch (address) {
219  case 0x04: return flash.peek(getDirectFlashAddr());
220  case 0x1f: return configRegs[0x00]; // mirror 'CardMDR' register
221  case 0x23: return configRegs[address] |
222  int(eeprom.read_DO(time));
223  case 0x2C: return '2';
224  case 0x2D: return '3';
225  case 0x2E: return '0';
226  default: return configRegs[address];
227  }
228  }
229 }
230 
231 byte Carnivore2::readConfigRegister(word address, EmuTime::param time)
232 {
233  address &= 0x3f;
234  if (address == 0x04) {
235  return flash.read(getDirectFlashAddr());
236  } else {
237  return peekConfigRegister(address, time);
238  }
239 }
240 
241 static constexpr float volumeLevel(byte volume)
242 {
243  constexpr byte tab[8] = {5, 6, 7, 8, 10, 12, 14, 16};
244  return tab[volume & 7] / 16.0f;
245 }
246 
247 void Carnivore2::writeSndLVL(byte value, EmuTime::param time)
248 {
249  configRegs[0x22] = value;
250  ym2413.setSoftwareVolume(volumeLevel(value >> 3), time);
251  scc .setSoftwareVolume(volumeLevel(value >> 0), time);
252 }
253 
254 void Carnivore2::writeCfgEEPR(byte value, EmuTime::param time)
255 {
256  configRegs[0x23] = value & 0x0e;
257  eeprom.write_DI (value & 2, time);
258  eeprom.write_CLK(value & 4, time);
259  eeprom.write_CS (value & 8, time);
260 }
261 
262 // check whether each of the 4 bit pairs are unique in the given byte x
263 [[nodiscard]] static bool bitPairsUnique(uint8_t x)
264 {
265  uint8_t seen = 0;
266  for (int i = 0; i < 4; ++i) {
267  seen |= 1 << (x & 3);
268  x >>= 2;
269  }
270  return seen == 0b1111;
271 }
272 
273 void Carnivore2::writeConfigRegister(word address, byte value, EmuTime::param time)
274 {
275  address &= 0x3f;
276  if ((0x05 <= address) && (address <= 0x1e)) {
277  // shadow registers
278  if (address == 0x05) value &= 0x7f;
279  if ((address == 0x1e) && ((value & 0x8f) == 0x0f)) return; // ignore write
280 
281  shadowConfigRegs[address] = value;
282  if (!delayedConfig()) configRegs[address] = value;
283  } else {
284  switch (address) {
285  case 0x03: configRegs[address] = value & 0x7f; break;
286  case 0x04: flash.write(getDirectFlashAddr(), value); break;
287  case 0x1f: configRegs[0x00] = value; break; // mirror 'CardMDR' register
288  case 0x20: configRegs[address] = value & 0x07; break;
289  case 0x22: writeSndLVL(value, time); break;
290  case 0x23: writeCfgEEPR(value, time); break;
291  //case 0x24: // TODO PSG key-click
292  case 0x28:
293  if (!bitPairsUnique(value)) {
295  "Illegal value of ", value,
296  "written to SLM_cfg register");
297  }
298  [[fallthrough]];
299  default: configRegs[address] = value; break;
300  }
301  }
302 }
303 
304 bool Carnivore2::isConfigReg(word address) const
305 {
306  if (configRegs[0x00] & 0x80) return false; // config regs disabled
307  unsigned base = ((configRegs[0x00] & 0x60) << 9) | 0xF80;
308  return (base <= address) && (address < (base + 0x40));
309 }
310 
311 std::pair<unsigned, byte> Carnivore2::decodeMultiMapper(word address) const
312 {
313  // check up to 4 possible banks
314  for (auto i : xrange(4)) {
315  const byte* base = configRegs + (i * 6) + 6; // points to R<i>Mask
316  byte mult = base[3];
317  if (mult & 8) continue; // bank disabled
318 
319  byte sizeCode = mult & 7;
320  if (sizeCode < 3) continue; // invalid size
321 
322  // check address
323  bool mirroringDisabled = mult & 0x40;
324  static constexpr byte checkMasks[2][8] = {
325  { 0x00, 0x00, 0x00, 0x30, 0x60, 0xc0, 0x80, 0x00 }, // mirroring enabled
326  { 0x00, 0x00, 0x00, 0xf0, 0xe0, 0xc0, 0x80, 0x00 }, // mirroring disabled
327  };
328  byte checkMask = checkMasks[mirroringDisabled][sizeCode];
329  if (((address >> 8) & checkMask) != (base[5] & checkMask)) continue;
330 
331  // found bank
332  byte bank = base[2] & base[4];
333  unsigned size = 512 << sizeCode; // 7->64k, 6->32k, ..., 3->4k
334  unsigned addr = (bank * size) | (address & (size - 1));
335  addr += configRegs[0x05] * 0x10000; // 64kB block offset
336  addr &= 0x7fffff; // 8MB
337  return {addr, mult};
338  }
339  return {unsigned(-1), byte(-1)};
340 }
341 
342 bool Carnivore2::sccAccess(word address) const
343 {
344  if (!sccEnabled()) return false;
345  if (sccMode & 0x20) {
346  // check scc plus
347  return (0xb800 <= address) && (address < 0xc000) &&
348  ((sccBank[3] & 0x80) == 0x80);
349  } else {
350  // check scc compatible
351  return (0x9800 <= address) && (address < 0xa000) &&
352  ((sccBank[2] & 0x3f) == 0x3f);
353  }
354 }
355 
356 byte Carnivore2::readMultiMapperSlot(word address, EmuTime::param time)
357 {
358  if (isConfigReg(address)) {
359  return readConfigRegister(address, time);
360  }
361  if (sccAccess(address)) {
362  return scc.readMem(address & 0xff, time);
363  }
364 
365  auto [addr, mult] = decodeMultiMapper(address);
366  if (addr == unsigned(-1)) return 0xff; // unmapped
367 
368  if (mult & 0x20) {
369  return ram[addr & 0x1fffff]; // 2MB
370  } else {
371  return flash.read(addr);
372  }
373 }
374 
375 byte Carnivore2::peekMultiMapperSlot(word address, EmuTime::param time) const
376 {
377  if (isConfigReg(address)) {
378  return peekConfigRegister(address, time);
379  }
380 
381  auto [addr, mult] = decodeMultiMapper(address);
382  if (addr == unsigned(-1)) return 0xff; // unmapped
383 
384  if (mult & 0x20) {
385  return ram[addr & 0x1fffff]; // 2MB
386  } else {
387  return flash.peek(addr);
388  }
389 }
390 
391 void Carnivore2::writeMultiMapperSlot(word address, byte value, EmuTime::param time)
392 {
393  if (isConfigReg(address)) {
394  // this blocks writes to switch-region and bank-region
395  return writeConfigRegister(address, value, time);
396  }
397 
398  // check (all) 4 bank switch regions
399  for (auto i : xrange(4)) {
400  byte* base = configRegs + (i * 6) + 6; // points to R<i>Mask
401  byte mask = base[0];
402  byte addr = base[1];
403  byte mult = base[3];
404  if (mult & 0x80) { // enable bit in R<i>Mult
405  if (((address >> 8) & mask) == (addr & mask)) {
406  // update actual+shadow reg
407  configRegs[(i * 6) + 6 + 2] = value;
408  shadowConfigRegs[(i * 6) + 6 + 2] = value;
409  }
410  }
411  }
412 
413  auto [addr, mult] = decodeMultiMapper(address);
414  if ((addr != unsigned(-1)) && (mult & 0x10)) { // write enable
415  if (mult & 0x20) {
416  ram[addr & 0x1fffff] = value; // 2MB
417  } else {
418  flash.write(addr, value);
419  }
420  }
421 
422  if (sccEnabled() && ((address | 1) == 0xbfff)) {
423  // write scc mode register (write-only)
424  sccMode = value;
425  scc.setChipMode((sccMode & 0x20) ? SCC::SCC_plusmode : SCC::SCC_Compatible);
426  }
427  if (((sccMode & 0x10) == 0x00) && // note: no check for sccEnabled()
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);
433  }
434 }
435 
436 byte Carnivore2::readIDESlot(word address, EmuTime::param time)
437 {
438  // TODO mirroring is different from SunriseIDE
439  if (ideRegsEnabled() && ((address & 0xfe00) == 0x7c00)) {
440  // 0x7c00-0x7dff IDE data register
441  switch (address & 1) {
442  case 0: { // data low
443  auto tmp = ideReadData(time);
444  ideRead = tmp >> 8;
445  return tmp & 0xff;
446  }
447  case 1: // data high
448  return ideRead;
449  }
450  }
451  if (ideRegsEnabled() && ((address & 0xff00) == 0x7e00)) {
452  // 0x7e00-0x7eff IDE registers
453  return ideReadReg(address & 0xf, time);
454  }
455  if ((0x4000 <= address) && (address < 0x8000)) {
456  // read IDE flash rom
457  unsigned addr = (address & 0x3fff) + (ideBank() * 0x4000) + 0x10000;
458  if (readBIOSfromRAM()) {
459  return ram[addr];
460  } else {
461  return flash.read(addr);
462  }
463  }
464  return 0xff;
465 }
466 
467 byte Carnivore2::peekIDESlot(word address, EmuTime::param /*time*/) const
468 {
469  if (ideRegsEnabled() && ((address & 0xfe00) == 0x7c00)) {
470  // 0x7c00-0x7dff IDE data register
471  return 0xff; // TODO not yet implemented
472  }
473  if (ideRegsEnabled() && ((address & 0xff00) == 0x7e00)) {
474  // 0x7e00-0x7eff IDE registers
475  return 0xff; // TODO not yet implemented
476  }
477  if ((0x4000 <= address) && (address < 0x8000)) {
478  // read IDE flash rom
479  unsigned addr = (address & 0x3fff) + (ideBank() * 0x4000) + 0x10000;
480  if (readBIOSfromRAM()) {
481  return ram[addr];
482  } else {
483  return flash.peek(addr);
484  }
485  }
486  return 0xff;
487 }
488 
489 void Carnivore2::writeIDESlot(word address, byte value, EmuTime::param time)
490 {
491  // TODO mirroring is different from SunriseIDE
492  if (address == 0x4104) {
493  ideControlReg = value;
494 
495  } else if (ideRegsEnabled() && ((address & 0xfe00) == 0x7c00)) {
496  // 0x7c00-0x7dff IDE data register
497  switch (address & 1) {
498  case 0: // data low
499  ideWrite = value;
500  break;
501  case 1: { // data high
502  word tmp = (value << 8) | ideWrite;
503  ideWriteData(tmp, time);
504  break;
505  }
506  }
507 
508  } else if (ideRegsEnabled() && ((address & 0xff00) == 0x7e00)) {
509  // 0x7e00-0x7eff IDE registers
510  ideWriteReg(address & 0xf, value, time);
511  }
512 }
513 
514 word Carnivore2::ideReadData(EmuTime::param time)
515 {
516  return ideDevices[ideSelectedDevice]->readData(time);
517 }
518 
519 void Carnivore2::ideWriteData(word value, EmuTime::param time)
520 {
521  ideDevices[ideSelectedDevice]->writeData(value, time);
522 }
523 
524 byte Carnivore2::ideReadReg(byte reg, EmuTime::param time)
525 {
526  if (reg == 14) reg = 7; // alternate status register
527 
528  if (ideSoftReset) {
529  if (reg == 7) { // read status
530  return 0xff; // busy
531  } else { // all others
532  return 0x7f; // don't care
533  }
534  } else {
535  if (reg == 0) {
536  return ideReadData(time) & 0xff;
537  } else {
538  auto result = ideDevices[ideSelectedDevice]->readReg(reg, time);
539  if (reg == 6) {
540  result = (result & 0xef) | (ideSelectedDevice ? 0x10 : 0x00);
541  }
542  return result;
543  }
544  }
545 }
546 
547 void Carnivore2::ideWriteReg(byte reg, byte value, EmuTime::param time)
548 {
549  if (ideSoftReset) {
550  if ((reg == 14) && !(value & 0x04)) {
551  // clear SRST
552  ideSoftReset = false;
553  }
554  // ignore all other writes
555  } else {
556  if (reg == 0) {
557  ideWriteData((value << 8) | value, time);
558  } else {
559  if ((reg == 14) && (value & 0x04)) {
560  // set SRST
561  ideSoftReset = true;
562  ideDevices[0]->reset(time);
563  ideDevices[1]->reset(time);
564  } else {
565  if (reg == 6) {
566  ideSelectedDevice = (value & 0x10) ? 1 : 0;
567  }
568  ideDevices[ideSelectedDevice]->writeReg(reg, value, time);
569  }
570  }
571  }
572 }
573 
574 bool Carnivore2::isMemmapControl(word address) const
575 {
576  return (port3C & 0x80) &&
577  (( (port3C & 0x08) && ((address & 0xc000) == 0x4000)) ||
578  (!(port3C & 0x08) && ((address & 0xc000) == 0x8000)));
579 }
580 
581 unsigned Carnivore2::getMemoryMapperAddress(word address) const
582 {
583  return (address & 0x3fff) +
584  0x4000 * memMapRegs[address >> 14] +
585  0x100000; // 2nd half of 2MB
586 }
587 
588 bool Carnivore2::isMemoryMapperWriteProtected(word address) const
589 {
590  byte page = address >> 14;
591  return (port3C & (1 << page)) != 0;
592 }
593 
594 byte Carnivore2::peekMemoryMapperSlot(word address) const
595 {
596  if (isMemmapControl(address)) {
597  switch (address & 0xff) {
598  case 0x3c:
599  return port3C;
600  case 0xfc: case 0xfd: case 0xfe: case 0xff:
601  return memMapRegs[address & 0x03];
602  }
603  }
604  return ram[getMemoryMapperAddress(address)];
605 }
606 
607 byte Carnivore2::readMemoryMapperSlot(word address)
608 {
609  return peekMemoryMapperSlot(address);
610 }
611 
612 void Carnivore2::writeMemoryMapperSlot(word address, byte value)
613 {
614  if (isMemmapControl(address)) {
615  switch (address & 0xff) {
616  case 0x3c:
617  value |= (value & 0x02) << 6; // TODO should be '(.. 0x20) << 2' ???
618  port3C = value;
619  return;
620  case 0xfc: case 0xfd: case 0xfe: case 0xff:
621  memMapRegs[address & 0x03] = value & 0x3f;
622  return;
623  }
624  }
625  if (!isMemoryMapperWriteProtected(address)) {
626  ram[getMemoryMapperAddress(address)] = value;
627  }
628 }
629 
630 byte Carnivore2::readFmPacSlot(word address)
631 {
632  if (address == 0x7ff6) {
633  return fmPacEnable; // enable
634  } else if (address == 0x7ff7) {
635  return fmPacBank; // bank
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) {
641  return fmPac5ffe; // always 0x4d
642  } else if (address == 0x5fff) {
643  return fmPac5fff; // always 0x69
644  } else {
645  return 0xff;
646  }
647  } else {
648  unsigned addr = (address & 0x3fff) + (0x4000 * fmPacBank) + 0x30000;
649  if (readBIOSfromRAM()) {
650  return ram[addr];
651  } else {
652  return flash.read(addr);
653  }
654  }
655  }
656  return 0xff;
657 }
658 
659 byte Carnivore2::peekFmPacSlot(word address) const
660 {
661  if (address == 0x7ff6) {
662  return fmPacEnable; // enable
663  } else if (address == 0x7ff7) {
664  return fmPacBank; // bank
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) {
670  return fmPac5ffe; // always 0x4d
671  } else if (address == 0x5fff) {
672  return fmPac5fff; // always 0x69
673  } else {
674  return 0xff;
675  }
676  } else {
677  unsigned addr = (address & 0x3fff) + (0x4000 * fmPacBank) + 0x30000;
678  if (readBIOSfromRAM()) {
679  return ram[addr];
680  } else {
681  return flash.peek(addr);
682  }
683  }
684  }
685  return 0xff;
686 }
687 
688 void Carnivore2::writeFmPacSlot(word address, byte value, EmuTime::param time)
689 {
690  if ((0x4000 <= address) && (address < 0x5ffe)) {
691  if (fmPacSramEnabled()) {
692  ram[(address & 0x1fff) | 0xfe000] = value;
693  }
694  } else if (address == 0x5ffe) {
695  fmPac5ffe = value;
696  } else if (address == 0x5fff) {
697  fmPac5fff = value;
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;
704  }
705 }
706 
707 byte Carnivore2::readIO(word port, EmuTime::param time)
708 {
709  return peekIO(port, time);
710 }
711 
712 byte Carnivore2::peekIO(word port, EmuTime::param /*time*/) const
713 {
714  // reading ports 0x3c, 0x7c, 0x7d has no effect
715  if (memMapReadEnabled() && ((port & 0xfc) == 0xfc)) {
716  // memory mapper registers
717  return getSelectedSegment(port & 3);
718  }
719  return 0xff;
720 }
721 
722 void Carnivore2::writeIO(word port, byte value, EmuTime::param time)
723 {
724  if (((port & 0xfe) == 0x7c) &&
725  (fmPacPortEnabled1() || fmPacPortEnabled2())) {
726  // fm-pac registers
727  ym2413.writePort(port & 1, value, time);
728  } else if (((port & 0xff) == 0x3c) && writePort3cEnabled()) {
729  // only change bit 7
730  port3C = (port3C & 0x7F) | (value & 0x80);
731 
732  } else if ((port & 0xfc) == 0xfc) {
733  // memory mapper registers
734  memMapRegs[port & 0x03] = value & 0x3f;
735  invalidateDeviceRWCache(0x4000 * (port & 0x03), 0x4000);
736  }
737 }
738 
739 byte Carnivore2::getSelectedSegment(byte page) const
740 {
741  return memMapRegs[page];
742 }
743 
744 // version 1: initial version
745 // version 2: removed fmPacRegSelect
746 template<typename Archive>
747 void Carnivore2::serialize(Archive& ar, unsigned /*version*/)
748 {
749  ar.template serializeBase<MSXDevice>(*this);
750  ar.serialize("flash", flash,
751  "ram", ram,
752  "eeprom", eeprom,
753  "configRegs", configRegs,
754  "shadowConfigRegs", shadowConfigRegs,
755  "subSlotReg", subSlotReg,
756  "port3C", port3C,
757 
758  "scc", scc,
759  "sccMode", sccMode,
760  "sccBank", sccBank);
761 
762  ar.serializePolymorphic("master", *ideDevices[0]);
763  ar.serializePolymorphic("slave", *ideDevices[1]);
764  ar.serialize("ideSoftReset", ideSoftReset,
765  "ideSelectedDevice", ideSelectedDevice,
766  "ideControlReg", ideControlReg,
767  "ideRead", ideRead,
768  "ideWrite", ideWrite,
769 
770  "memMapRegs", memMapRegs,
771 
772  "ym2413", ym2413,
773  "fmPacEnable", fmPacEnable,
774  "fmPacBank", fmPacBank,
775  "fmPac5ffe", fmPac5ffe,
776  "fmPac5fff", fmPac5fff);
777 
778  if constexpr (Archive::IS_LOADER) {
779  auto time = getCurrentTime();
780  writeSndLVL (configRegs[0x22], time);
781  writeCfgEEPR(configRegs[0x23], time);
782  }
783 }
786 
787 } // namespace openmsx
Definition: one_of.hh:7
void write(unsigned address, byte value)
Definition: AmdFlash.cc:265
byte peek(unsigned address) const
Definition: AmdFlash.cc:212
byte read(unsigned address) const
Definition: AmdFlash.cc:248
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
Definition: Carnivore2.cc:712
void reset(EmuTime::param time) override
This method is called on reset.
Definition: Carnivore2.cc:54
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.
Definition: Carnivore2.cc:177
void serialize(Archive &ar, unsigned version)
Definition: Carnivore2.cc:747
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
Definition: Carnivore2.cc:707
void globalRead(word address, EmuTime::param time) override
Global reads.
Definition: Carnivore2.cc:104
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
Definition: Carnivore2.cc:47
~Carnivore2() override
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
Definition: Carnivore2.cc:149
Carnivore2(const DeviceConfig &config)
Definition: Carnivore2.cc:28
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: Carnivore2.cc:722
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
Definition: Carnivore2.cc:163
byte getSelectedSegment(byte page) const override
Definition: Carnivore2.cc:739
void printWarning(std::string_view message)
Definition: CliComm.cc:10
const XMLElement * findChild(std::string_view name) const
Definition: DeviceConfig.cc:66
void write_CS(bool value, EmuTime::param time)
Definition: EEPROM_93C46.cc:76
void write_CLK(bool value, EmuTime::param time)
Definition: EEPROM_93C46.cc:91
bool read_DO(EmuTime::param time) const
Definition: EEPROM_93C46.cc:65
void write_DI(bool value, EmuTime::param time)
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition: MSXDevice.hh:33
CliComm & getCliComm() const
Definition: MSXDevice.cc:142
MSXCPU & getCPU() const
Definition: MSXDevice.cc:130
void invalidateDeviceRWCache()
Calls MSXCPUInterface::invalidateXXCache() for the specific (part of) the slot that this device is lo...
Definition: MSXDevice.hh:209
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:126
void writeMem(byte address, byte value, EmuTime::param time)
Definition: SCC.cc:285
void setChipMode(ChipMode newMode)
Definition: SCC.cc:183
void powerUp(EmuTime::param time)
Definition: SCC.cc:141
byte readMem(byte address, EmuTime::param time)
Definition: SCC.cc:193
@ SCC_plusmode
Definition: SCC.hh:14
@ SCC_Compatible
Definition: SCC.hh:14
void reset(EmuTime::param time)
Definition: SCC.cc:173
void setSoftwareVolume(float volume, EmuTime::param time)
Change the 'software volume' of this sound device.
Definition: SoundDevice.cc:144
void writePort(bool port, byte value, EmuTime::param time)
Definition: YM2413.cc:85
void reset(EmuTime::param time)
Definition: YM2413.cc:79
constexpr void fill(ForwardIt first, ForwardIt last, const T &value)
Definition: cstd.hh:111
std::unique_ptr< IDEDevice > create(const DeviceConfig &config)
std::string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:741
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
AmdFlash::SectorInfo Info
Definition: RomManbow2.cc:17
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
constexpr KeyMatrixPosition x
Keyboard bindings.
Definition: Keyboard.cc:118
constexpr nibble mask[4][13]
Definition: RP5C01.cc:34
constexpr void iota(ForwardIt first, ForwardIt last, T value)
Definition: ranges.hh:233
size_t size(std::string_view utf8)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:998
constexpr auto xrange(T e)
Definition: xrange.hh:155