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