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