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  "ram", ram,
702  "eeprom", eeprom,
703  "configRegs", configRegs,
704  "shadowConfigRegs", shadowConfigRegs,
705  "subSlotReg", subSlotReg,
706  "port3C", port3C,
707 
708  "scc", scc,
709  "sccMode", sccMode,
710  "sccBank", sccBank);
711 
712  ar.serializePolymorphic("master", *ideDevices[0]);
713  ar.serializePolymorphic("slave", *ideDevices[1]);
714  ar.serialize("ideSoftReset", ideSoftReset,
715  "ideSelectedDevice", ideSelectedDevice,
716  "ideControlReg", ideControlReg,
717  "ideRead", ideRead,
718  "ideWrite", ideWrite,
719 
720  "memMapRegs", memMapRegs,
721 
722  "ym2413", ym2413,
723  "fmPacEnable", fmPacEnable,
724  "fmPacBank", fmPacBank,
725  "fmPac5ffe", fmPac5ffe,
726  "fmPac5fff", fmPac5fff,
727  "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:141
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:1006
~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