openMSX
RomFactory.cc
Go to the documentation of this file.
1 #include "RomFactory.hh"
2 #include "RomTypes.hh"
3 #include "RomInfo.hh"
4 #include "RomPageNN.hh"
5 #include "RomPlain.hh"
6 #include "RomDRAM.hh"
7 #include "RomGeneric8kB.hh"
8 #include "RomGeneric16kB.hh"
9 #include "RomKonami.hh"
10 #include "RomKonamiSCC.hh"
12 #include "RomAscii8kB.hh"
13 #include "RomAscii8_8.hh"
14 #include "RomAscii16kB.hh"
15 #include "RomMSXWrite.hh"
16 #include "RomPadial8kB.hh"
17 #include "RomPadial16kB.hh"
18 #include "RomSuperLodeRunner.hh"
19 #include "RomSuperSwangi.hh"
20 #include "RomMitsubishiMLTS2.hh"
21 #include "RomMSXDOS2.hh"
22 #include "RomAscii16_2.hh"
23 #include "RomRType.hh"
24 #include "RomCrossBlaim.hh"
25 #include "RomHarryFox.hh"
26 #include "RomPanasonic.hh"
27 #include "RomNational.hh"
28 #include "RomMajutsushi.hh"
29 #include "RomSynthesizer.hh"
30 #include "RomPlayBall.hh"
31 #include "RomNettouYakyuu.hh"
32 #include "RomGameMaster2.hh"
33 #include "RomHalnote.hh"
34 #include "RomZemina80in1.hh"
35 #include "RomZemina90in1.hh"
36 #include "RomZemina126in1.hh"
37 #include "RomHolyQuran.hh"
38 #include "RomHolyQuran2.hh"
39 #include "RomFSA1FM.hh"
40 #include "RomManbow2.hh"
41 #include "RomMatraInk.hh"
42 #include "RomMatraCompilation.hh"
43 #include "RomArc.hh"
44 #include "ROMHunterMk2.hh"
45 #include "MegaFlashRomSCCPlus.hh"
46 #include "ReproCartridgeV1.hh"
47 #include "ReproCartridgeV2.hh"
49 #include "RomDooly.hh"
50 #include "RomMSXtra.hh"
51 #include "RomRamFile.hh"
52 #include "RomColecoMegaCart.hh"
53 #include "RomMultiRom.hh"
54 #include "Rom.hh"
55 #include "Reactor.hh"
56 #include "MSXMotherBoard.hh"
57 #include "RomDatabase.hh"
58 #include "DeviceConfig.hh"
59 #include "XMLElement.hh"
60 #include "MSXException.hh"
61 #include <memory>
62 
63 using std::make_unique;
64 using std::move;
65 using std::string;
66 using std::unique_ptr;
67 
68 namespace openmsx {
69 namespace RomFactory {
70 
71 static RomType guessRomType(const Rom& rom)
72 {
73  int size = rom.getSize();
74  if (size == 0) {
75  return ROM_NORMAL;
76  }
77  const byte* data = &rom[0];
78 
79  if (size < 0x10000) {
80  if ((size <= 0x4000) &&
81  (data[0] == 'A') && (data[1] == 'B')) {
82  word initAddr = data[2] + 256 * data[3];
83  word textAddr = data[8] + 256 * data[9];
84  if ((textAddr & 0xC000) == 0x8000) {
85  if ((initAddr == 0) ||
86  (((initAddr & 0xC000) == 0x8000) &&
87  (data[initAddr & (size - 1)] == 0xC9))) {
88  return ROM_PAGE2;
89  }
90  }
91  }
92  // not correct for Konami-DAC, but does this really need
93  // to be correct for _every_ rom?
94  return ROM_MIRRORED;
95  } else if (size == 0x10000 && !((data[0] == 'A') && (data[1] == 'B'))) {
96  // 64 kB ROMs can be plain or memory mapped...
97  // check here for plain, if not, try the auto detection
98  // (thanks for the hint, hap)
99  return ROM_MIRRORED;
100  } else {
101  // GameCartridges do their bankswitching by using the Z80
102  // instruction ld(nn),a in the middle of program code. The
103  // adress nn depends upon the GameCartridge mappertype used.
104  // To guess which mapper it is, we will look how much writes
105  // with this instruction to the mapper-registers-addresses
106  // occur.
107 
108  unsigned typeGuess[ROM_END_OF_UNORDERED_LIST] = {}; // 0-initialized
109  for (int i = 0; i < size - 3; ++i) {
110  if (data[i] == 0x32) {
111  word value = data[i + 1] + (data[i + 2] << 8);
112  switch (value) {
113  case 0x5000:
114  case 0x9000:
115  case 0xb000:
116  typeGuess[ROM_KONAMI_SCC]++;
117  break;
118  case 0x4000:
119  typeGuess[ROM_KONAMI]++;
120  break;
121  case 0x8000:
122  case 0xa000:
123  typeGuess[ROM_KONAMI]++;
124  break;
125  case 0x6800:
126  case 0x7800:
127  typeGuess[ROM_ASCII8]++;
128  break;
129  case 0x6000:
130  typeGuess[ROM_KONAMI]++;
131  typeGuess[ROM_ASCII8]++;
132  typeGuess[ROM_ASCII16]++;
133  break;
134  case 0x7000:
135  typeGuess[ROM_KONAMI_SCC]++;
136  typeGuess[ROM_ASCII8]++;
137  typeGuess[ROM_ASCII16]++;
138  break;
139  case 0x77ff:
140  typeGuess[ROM_ASCII16]++;
141  break;
142  }
143  }
144  }
145  if (typeGuess[ROM_ASCII8]) typeGuess[ROM_ASCII8]--; // -1 -> max_int
146  RomType type = ROM_GENERIC_8KB;
147  for (int i = 0; i < ROM_END_OF_UNORDERED_LIST; ++i) {
148  // debug: fprintf(stderr, "%d: %d\n", i, typeGuess[i]);
149  if (typeGuess[i] && (typeGuess[i] >= typeGuess[type])) {
150  type = static_cast<RomType>(i);
151  }
152  }
153  return type;
154  }
155 }
156 
157 unique_ptr<MSXDevice> create(const DeviceConfig& config)
158 {
159  Rom rom(config.getAttribute("id"), "rom", config);
160 
161  // Get specified mapper type from the config.
162  RomType type;
163  // if no type is mentioned, we assume 'mirrored' which works for most
164  // plain ROMs...
165  string_view typestr = config.getChildData("mappertype", "Mirrored");
166  if (typestr == "auto") {
167  // First check whether the (possibly patched) SHA1 is in the DB
168  const RomInfo* romInfo = config.getReactor().getSoftwareDatabase().fetchRomInfo(rom.getSHA1());
169  // If not found, try the original SHA1 in the DB
170  if (!romInfo) {
171  romInfo = config.getReactor().getSoftwareDatabase().fetchRomInfo(rom.getOriginalSHA1());
172  }
173  // If still not found, guess the mapper type
174  if (!romInfo) {
175  auto machineType = config.getMotherBoard().getMachineType();
176  if (machineType == "Coleco") {
177  unsigned size = rom.getSize();
178  if ((size == 128*1024) || (size == 256*1024) ||
179  (size == 512*1024) || (size == 1024*1024)) {
180  type = ROM_COLECOMEGACART;
181  }
182  else {
183  type = ROM_PAGE23;
184  }
185  } else {
186  type = guessRomType(rom);
187  }
188  } else {
189  type = romInfo->getRomType();
190  }
191  } else {
192  // Use mapper type from config, even if this overrides DB.
193  type = RomInfo::nameToRomType(typestr);
194  if (type == ROM_UNKNOWN) {
195  throw MSXException("Unknown mappertype: ", typestr);
196  }
197  }
198 
199  // Store actual detected mapper type in config (override the possible
200  // 'auto' value). This way we're sure that on savestate/loadstate we're
201  // using the same mapper type (for example when the user's rom-database
202  // was updated).
203  // We do it at this point so that constructors used below can use this
204  // information for warning messages etc.
205  auto& writableConfig = const_cast<XMLElement&>(*config.getXML());
206  writableConfig.setChildData("mappertype", RomInfo::romTypeToName(type).str());
207 
208  unique_ptr<MSXRom> result;
209  switch (type) {
210  case ROM_MIRRORED:
211  result = make_unique<RomPlain>(
212  config, move(rom), RomPlain::MIRRORED);
213  break;
214  case ROM_NORMAL:
215  result = make_unique<RomPlain>(
216  config, move(rom), RomPlain::NOT_MIRRORED);
217  break;
218  case ROM_MIRRORED0000:
219  case ROM_MIRRORED4000:
220  case ROM_MIRRORED8000:
221  case ROM_MIRROREDC000:
222  result = make_unique<RomPlain>(
223  config, move(rom), RomPlain::MIRRORED,
224  (type & 7) * 0x2000);
225  break;
226  case ROM_NORMAL0000:
227  case ROM_NORMAL4000:
228  case ROM_NORMAL8000:
229  case ROM_NORMALC000:
230  result = make_unique<RomPlain>(
231  config, move(rom), RomPlain::NOT_MIRRORED,
232  (type & 7) * 0x2000);
233  break;
234  case ROM_PAGE0:
235  case ROM_PAGE1:
236  case ROM_PAGE01:
237  case ROM_PAGE2:
238  case ROM_PAGE12:
239  case ROM_PAGE012:
240  case ROM_PAGE3:
241  case ROM_PAGE23:
242  case ROM_PAGE123:
243  case ROM_PAGE0123:
244  result = make_unique<RomPageNN>(config, move(rom), type & 0xF);
245  break;
246  case ROM_DRAM:
247  result = make_unique<RomDRAM>(config, move(rom));
248  break;
249  case ROM_GENERIC_8KB:
250  result = make_unique<RomGeneric8kB>(config, move(rom));
251  break;
252  case ROM_GENERIC_16KB:
253  result = make_unique<RomGeneric16kB>(config, move(rom));
254  break;
255  case ROM_KONAMI_SCC:
256  result = make_unique<RomKonamiSCC>(config, move(rom));
257  break;
258  case ROM_KONAMI:
259  result = make_unique<RomKonami>(config, move(rom));
260  break;
261  case ROM_KBDMASTER:
262  result = make_unique<RomKonamiKeyboardMaster>(config, move(rom));
263  break;
264  case ROM_ASCII8:
265  result = make_unique<RomAscii8kB>(config, move(rom));
266  break;
267  case ROM_ASCII16:
268  result = make_unique<RomAscii16kB>(config, move(rom));
269  break;
270  case ROM_MSXWRITE:
271  result = make_unique<RomMSXWrite>(config, move(rom));
272  break;
273  case ROM_PADIAL8:
274  result = make_unique<RomPadial8kB>(config, move(rom));
275  break;
276  case ROM_PADIAL16:
277  result = make_unique<RomPadial16kB>(config, move(rom));
278  break;
279  case ROM_SUPERLODERUNNER:
280  result = make_unique<RomSuperLodeRunner>(config, move(rom));
281  break;
282  case ROM_SUPERSWANGI:
283  result = make_unique<RomSuperSwangi>(config, move(rom));
284  break;
285  case ROM_MITSUBISHIMLTS2:
286  result = make_unique<RomMitsubishiMLTS2>(config, move(rom));
287  break;
288  case ROM_MSXDOS2:
289  result = make_unique<RomMSXDOS2>(config, move(rom));
290  break;
291  case ROM_R_TYPE:
292  result = make_unique<RomRType>(config, move(rom));
293  break;
294  case ROM_CROSS_BLAIM:
295  result = make_unique<RomCrossBlaim>(config, move(rom));
296  break;
297  case ROM_HARRY_FOX:
298  result = make_unique<RomHarryFox>(config, move(rom));
299  break;
300  case ROM_ASCII8_8:
301  result = make_unique<RomAscii8_8>(
302  config, move(rom), RomAscii8_8::ASCII8_8);
303  break;
304  case ROM_ASCII8_32:
305  result = make_unique<RomAscii8_8>(
306  config, move(rom), RomAscii8_8::ASCII8_32);
307  break;
308  case ROM_ASCII8_2:
309  result = make_unique<RomAscii8_8>(
310  config, move(rom), RomAscii8_8::ASCII8_2);
311  break;
312  case ROM_KOEI_8:
313  result = make_unique<RomAscii8_8>(
314  config, move(rom), RomAscii8_8::KOEI_8);
315  break;
316  case ROM_KOEI_32:
317  result = make_unique<RomAscii8_8>(
318  config, move(rom), RomAscii8_8::KOEI_32);
319  break;
320  case ROM_WIZARDRY:
321  result = make_unique<RomAscii8_8>(
322  config, move(rom), RomAscii8_8::WIZARDRY);
323  break;
324  case ROM_ASCII16_2:
325  result = make_unique<RomAscii16_2>(config, move(rom), RomAscii16_2::ASCII16_2);
326  break;
327  case ROM_ASCII16_8:
328  result = make_unique<RomAscii16_2>(config, move(rom), RomAscii16_2::ASCII16_8);
329  break;
330  case ROM_GAME_MASTER2:
331  result = make_unique<RomGameMaster2>(config, move(rom));
332  break;
333  case ROM_PANASONIC:
334  result = make_unique<RomPanasonic>(config, move(rom));
335  break;
336  case ROM_NATIONAL:
337  result = make_unique<RomNational>(config, move(rom));
338  break;
339  case ROM_MAJUTSUSHI:
340  result = make_unique<RomMajutsushi>(config, move(rom));
341  break;
342  case ROM_SYNTHESIZER:
343  result = make_unique<RomSynthesizer>(config, move(rom));
344  break;
345  case ROM_PLAYBALL:
346  result = make_unique<RomPlayBall>(config, move(rom));
347  break;
348  case ROM_NETTOU_YAKYUU:
349  result = make_unique<RomNettouYakyuu>(config, move(rom));
350  break;
351  case ROM_HALNOTE:
352  result = make_unique<RomHalnote>(config, move(rom));
353  break;
354  case ROM_ZEMINA80IN1:
355  result = make_unique<RomZemina80in1>(config, move(rom));
356  break;
357  case ROM_ZEMINA90IN1:
358  result = make_unique<RomZemina90in1>(config, move(rom));
359  break;
360  case ROM_ZEMINA126IN1:
361  result = make_unique<RomZemina126in1>(config, move(rom));
362  break;
363  case ROM_HOLY_QURAN:
364  result = make_unique<RomHolyQuran>(config, move(rom));
365  break;
366  case ROM_HOLY_QURAN2:
367  result = make_unique<RomHolyQuran2>(config, move(rom));
368  break;
369  case ROM_FSA1FM1:
370  result = make_unique<RomFSA1FM1>(config, move(rom));
371  break;
372  case ROM_FSA1FM2:
373  result = make_unique<RomFSA1FM2>(config, move(rom));
374  break;
375  case ROM_MANBOW2:
376  case ROM_MANBOW2_2:
377  case ROM_HAMARAJANIGHT:
378  case ROM_MEGAFLASHROMSCC:
379  result = make_unique<RomManbow2>(config, move(rom), type);
380  break;
381  case ROM_MATRAINK:
382  result = make_unique<RomMatraInk>(config, move(rom));
383  break;
385  result = make_unique<RomMatraCompilation>(config, move(rom));
386  break;
387  case ROM_ARC:
388  result = make_unique<RomArc>(config, move(rom));
389  break;
390  case ROM_ROMHUNTERMK2:
391  result = make_unique<ROMHunterMk2>(config, move(rom));
392  break;
394  result = make_unique<MegaFlashRomSCCPlus>(config, move(rom));
395  break;
397  result = make_unique<ReproCartridgeV1>(config, move(rom));
398  break;
400  result = make_unique<ReproCartridgeV2>(config, move(rom));
401  break;
403  result = make_unique<KonamiUltimateCollection>(config, move(rom));
404  break;
405  case ROM_DOOLY:
406  result = make_unique<RomDooly>(config, move(rom));
407  break;
408  case ROM_MSXTRA:
409  result = make_unique<RomMSXtra>(config, move(rom));
410  break;
411  case ROM_MULTIROM:
412  result = make_unique<RomMultiRom>(config, move(rom));
413  break;
414  case ROM_RAMFILE:
415  result = make_unique<RomRamFile>(config, move(rom));
416  break;
417  case ROM_COLECOMEGACART:
418  result = make_unique<RomColecoMegaCart>(config, move(rom));
419  break;
420  default:
421  throw MSXException("Unknown ROM type");
422  }
423 
424  return result;
425 }
426 
427 } // namespace RomFactory
428 } // namespace openmsx
const std::string & getChildData(string_view name) const
Definition: DeviceConfig.cc:43
const std::string & getAttribute(string_view attName) const
Definition: DeviceConfig.cc:65
const Sha1Sum & getSHA1() const
Definition: Rom.cc:362
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
static string_view romTypeToName(RomType type)
Definition: RomInfo.cc:194
RomDatabase & getSoftwareDatabase()
Definition: Reactor.cc:296
constexpr auto data(C &c) -> decltype(c.data())
Definition: span.hh:69
Reactor & getReactor() const
Definition: DeviceConfig.cc:30
const Sha1Sum & getOriginalSHA1() const
Definition: Rom.cc:354
RomType getRomType() const
Definition: RomInfo.hh:50
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
const RomInfo * fetchRomInfo(const Sha1Sum &sha1sum) const
Lookup an entry in the database by sha1sum.
Definition: RomDatabase.cc:615
void setChildData(string_view name, std::string value)
Definition: XMLElement.cc:193
unsigned getSize() const
Definition: Rom.hh:32
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
This class implements a (close approximation) of the std::string_view class.
Definition: string_view.hh:16
std::string getMachineType() const
const XMLElement * getXML() const
Definition: DeviceConfig.hh:47
static RomType nameToRomType(string_view name)
Definition: RomInfo.cc:188
unique_ptr< MSXDevice > create(const DeviceConfig &config)
Definition: RomFactory.cc:157
constexpr auto size(const C &c) -> decltype(c.size())
Definition: span.hh:62
MSXMotherBoard & getMotherBoard() const
Definition: DeviceConfig.cc:13