openMSX
MSXSCCPlusCart.cc
Go to the documentation of this file.
1 // Note: this device is actually called SCC-I. But this would take a lot of
2 // renaming, which isn't worth it right now. TODO rename this :)
3 
4 #include "MSXSCCPlusCart.hh"
5 #include "File.hh"
6 #include "FileContext.hh"
7 #include "FileException.hh"
8 #include "XMLElement.hh"
9 #include "CacheLine.hh"
10 #include "ranges.hh"
11 #include "serialize.hh"
12 
13 namespace openmsx {
14 
16  : MSXDevice(config)
17  , ram(config, getName() + " RAM", "SCC+ RAM", 0x20000)
18  , scc(getName(), config, getCurrentTime(), SCC::SCC_Compatible)
19  , romBlockDebug(*this, mapper, 0x4000, 0x8000, 13)
20 {
21  if (const XMLElement* fileElem = config.findChild("filename")) {
22  // read the rom file
23  const std::string& filename = fileElem->getData();
24  try {
25  File file(config.getFileContext().resolve(filename));
26  auto size = std::min<size_t>(file.getSize(), ram.getSize());
27  file.read(&ram[0], size);
28  } catch (FileException&) {
29  throw MSXException("Error reading file: ", filename);
30  }
31  }
32  string_view subtype = config.getChildData("subtype", "expanded");
33  if (subtype == "Snatcher") {
34  mapperMask = 0x0F;
35  lowRAM = true;
36  highRAM = false;
37  } else if (subtype == "SD-Snatcher") {
38  mapperMask = 0x0F;
39  lowRAM = false;
40  highRAM = true;
41  } else if (subtype == "mirrored") {
42  mapperMask = 0x07;
43  lowRAM = true;
44  highRAM = true;
45  } else {
46  // subtype "expanded", and all others
47  mapperMask = 0x0F;
48  lowRAM = true;
49  highRAM = true;
50  }
51 
52  // make valgrind happy
53  ranges::fill(isRamSegment, true);
54  ranges::fill(mapper, 0);
55 
57 }
58 
59 void MSXSCCPlusCart::powerUp(EmuTime::param time)
60 {
61  scc.powerUp(time);
62  reset(time);
63 }
64 
65 void MSXSCCPlusCart::reset(EmuTime::param time)
66 {
67  setModeRegister(0);
68  setMapper(0, 0);
69  setMapper(1, 1);
70  setMapper(2, 2);
71  setMapper(3, 3);
72  scc.reset(time);
73 }
74 
75 
76 byte MSXSCCPlusCart::readMem(word addr, EmuTime::param time)
77 {
78  byte result;
79  if (((enable == EN_SCC) && (0x9800 <= addr) && (addr < 0xA000)) ||
80  ((enable == EN_SCCPLUS) && (0xB800 <= addr) && (addr < 0xC000))) {
81  result = scc.readMem(addr & 0xFF, time);
82  } else {
83  result = MSXSCCPlusCart::peekMem(addr, time);
84  }
85  return result;
86 }
87 
88 byte MSXSCCPlusCart::peekMem(word addr, EmuTime::param time) const
89 {
90  // modeRegister can not be read!
91  if (((enable == EN_SCC) && (0x9800 <= addr) && (addr < 0xA000)) ||
92  ((enable == EN_SCCPLUS) && (0xB800 <= addr) && (addr < 0xC000))) {
93  // SCC visible in 0x9800 - 0x9FFF
94  // SCC+ visible in 0xB800 - 0xBFFF
95  return scc.peekMem(addr & 0xFF, time);
96  } else if ((0x4000 <= addr) && (addr < 0xC000)) {
97  // SCC(+) enabled/disabled but not requested so memory stuff
98  return internalMemoryBank[(addr >> 13) - 2][addr & 0x1FFF];
99  } else {
100  // outside memory range
101  return 0xFF;
102  }
103 }
104 
106 {
107  if (((enable == EN_SCC) && (0x9800 <= start) && (start < 0xA000)) ||
108  ((enable == EN_SCCPLUS) && (0xB800 <= start) && (start < 0xC000))) {
109  // SCC visible in 0x9800 - 0x9FFF
110  // SCC+ visible in 0xB800 - 0xBFFF
111  return nullptr;
112  } else if ((0x4000 <= start) && (start < 0xC000)) {
113  // SCC(+) enabled/disabled but not requested so memory stuff
114  return &internalMemoryBank[(start >> 13) - 2][start & 0x1FFF];
115  } else {
116  // outside memory range
117  return unmappedRead;
118  }
119 }
120 
121 
122 void MSXSCCPlusCart::writeMem(word address, byte value, EmuTime::param time)
123 {
124  if ((address < 0x4000) || (0xC000 <= address)) {
125  // outside memory range
126  return;
127  }
128 
129  // Mode register is mapped upon 0xBFFE and 0xBFFF
130  if ((address | 0x0001) == 0xBFFF) {
131  setModeRegister(value);
132  return;
133  }
134 
135  // Write to RAM
136  int regio = (address >> 13) - 2;
137  if (isRamSegment[regio]) {
138  // According to Sean Young
139  // when the regio's are in RAM mode you can read from
140  // the SCC(+) but not write to them
141  // => we assume a write to the memory but maybe
142  // they are just discarded
143  // TODO check this out => ask Sean...
144  if (isMapped[regio]) {
145  internalMemoryBank[regio][address & 0x1FFF] = value;
146  }
147  return;
148  }
149 
150  /* Write to bankswitching registers
151  * The address to change banks:
152  * bank 1: 0x5000 - 0x57FF (0x5000 used)
153  * bank 2: 0x7000 - 0x77FF (0x7000 used)
154  * bank 3: 0x9000 - 0x97FF (0x9000 used)
155  * bank 4: 0xB000 - 0xB7FF (0xB000 used)
156  */
157  if ((address & 0x1800) == 0x1000) {
158  setMapper(regio, value);
159  return;
160  }
161 
162  // call writeMemInterface of SCC if needed
163  switch (enable) {
164  case EN_NONE:
165  // do nothing
166  break;
167  case EN_SCC:
168  if ((0x9800 <= address) && (address < 0xA000)) {
169  scc.writeMem(address & 0xFF, value, time);
170  }
171  break;
172  case EN_SCCPLUS:
173  if ((0xB800 <= address) && (address < 0xC000)) {
174  scc.writeMem(address & 0xFF, value, time);
175  }
176  break;
177  }
178 }
179 
181 {
182  if ((0x4000 <= start) && (start < 0xC000)) {
183  if (start == (0xBFFF & CacheLine::HIGH)) {
184  return nullptr;
185  }
186  int regio = (start >> 13) - 2;
187  if (isRamSegment[regio] && isMapped[regio]) {
188  return &internalMemoryBank[regio][start & 0x1FFF];
189  }
190  return nullptr;
191  }
192  return unmappedWrite;
193 }
194 
195 
196 void MSXSCCPlusCart::setMapper(int regio, byte value)
197 {
198  mapper[regio] = value;
199  value &= mapperMask;
200 
201  byte* block;
202  if ((!lowRAM && (value < 8)) ||
203  (!highRAM && (value >= 8))) {
204  block = unmappedRead;
205  isMapped[regio] = false;
206  } else {
207  block = &ram[0x2000 * value];
208  isMapped[regio] = true;
209  }
210 
211  checkEnable(); // invalidateMemCache() done below
212  internalMemoryBank[regio] = block;
213  invalidateMemCache(0x4000 + regio * 0x2000, 0x2000);
214 }
215 
216 void MSXSCCPlusCart::setModeRegister(byte value)
217 {
218  modeRegister = value;
219  checkEnable(); // invalidateMemCache() done below
220 
221  if (modeRegister & 0x20) {
223  } else {
225  }
226 
227  if (modeRegister & 0x10) {
228  isRamSegment[0] = true;
229  isRamSegment[1] = true;
230  isRamSegment[2] = true;
231  isRamSegment[3] = true;
232  } else {
233  isRamSegment[0] = (modeRegister & 0x01) == 0x01;
234  isRamSegment[1] = (modeRegister & 0x02) == 0x02;
235  isRamSegment[2] = (modeRegister & 0x24) == 0x24; // extra requirement: SCC+ mode
236  isRamSegment[3] = false;
237  }
238  invalidateMemCache(0x4000, 0x8000);
239 }
240 
241 void MSXSCCPlusCart::checkEnable()
242 {
243  if ((modeRegister & 0x20) && (mapper[3] & 0x80)) {
244  enable = EN_SCCPLUS;
245  } else if ((!(modeRegister & 0x20)) && ((mapper[2] & 0x3F) == 0x3F)) {
246  enable = EN_SCC;
247  } else {
248  enable = EN_NONE;
249  }
250 }
251 
252 
253 template<typename Archive>
254 void MSXSCCPlusCart::serialize(Archive& ar, unsigned /*version*/)
255 {
256  // These are constants:
257  // mapperMask, lowRAM, highRAM
258 
259  // only serialize that part of the Ram object that's actually
260  // present in the cartridge
261  unsigned ramSize = (lowRAM && highRAM && (mapperMask == 0xF))
262  ? 0x20000 : 0x10000;
263  unsigned ramBase = lowRAM ? 0x00000 : 0x10000;
264  ar.serialize_blob("ram", &ram[ramBase], ramSize);
265 
266  ar.serialize("scc", scc,
267  "mapper", mapper,
268  "mode", modeRegister);
269 
270  if (ar.isLoader()) {
271  // recalculate: isMapped[4], internalMemoryBank[4]
272  for (int i = 0; i < 4; ++i) {
273  setMapper(i, mapper[i]);
274  }
275  // recalculate: enable, isRamSegment[4]
276  setModeRegister(modeRegister);
277  }
278 }
281 
282 } // namespace openmsx
const std::string & getChildData(string_view name) const
Definition: DeviceConfig.cc:43
void setChipMode(ChipMode newMode)
Definition: SCC.cc:182
void writeMem(byte address, byte value, EmuTime::param time)
Definition: SCC.cc:289
const XMLElement * findChild(string_view name) const
Definition: DeviceConfig.cc:61
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
void reset(EmuTime::param time)
Definition: SCC.cc:172
const FileContext & getFileContext() const
Definition: DeviceConfig.cc:9
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
std::string resolve(string_view filename) const
Definition: FileContext.cc:76
void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:191
MSXSCCPlusCart(const DeviceConfig &config)
void reset(EmuTime::param time) override
This method is called on reset.
byte readMem(byte address, EmuTime::param time)
Definition: SCC.cc:192
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:133
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
const byte * getReadCacheLine(word start) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading...
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX...
Definition: MSXDevice.hh:31
byte peekMem(byte address, EmuTime::param time) const
Definition: SCC.cc:205
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:274
This class implements a (close approximation) of the std::string_view class.
Definition: string_view.hh:16
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1006
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:275
string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:589
void powerUp(EmuTime::param time)
Definition: SCC.cc:140
byte * getWriteCacheLine(word start) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing...
constexpr auto size(const C &c) -> decltype(c.size())
Definition: span.hh:62
void invalidateMemCache(word start, unsigned size)
Invalidate CPU memory-mapping cache.
Definition: MSXDevice.cc:458
void serialize(Archive &ar, unsigned version)
unsigned getSize() const
Definition: Ram.hh:33
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.