Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libsnes/bsnes/nall/snes/cartridge.hpp
2 views
1
#ifndef NALL_SNES_CARTRIDGE_HPP
2
#define NALL_SNES_CARTRIDGE_HPP
3
4
namespace nall {
5
6
class SnesCartridge {
7
public:
8
string markup;
9
inline SnesCartridge(const uint8_t *data, unsigned size);
10
11
//private:
12
inline void read_header(const uint8_t *data, unsigned size);
13
inline unsigned find_header(const uint8_t *data, unsigned size);
14
inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr);
15
inline unsigned gameboy_ram_size(const uint8_t *data, unsigned size);
16
inline bool gameboy_has_rtc(const uint8_t *data, unsigned size);
17
18
enum HeaderField {
19
CartName = 0x00,
20
Mapper = 0x15,
21
RomType = 0x16,
22
RomSize = 0x17,
23
RamSize = 0x18,
24
CartRegion = 0x19,
25
Company = 0x1a,
26
Version = 0x1b,
27
Complement = 0x1c, //inverse checksum
28
Checksum = 0x1e,
29
ResetVector = 0x3c,
30
};
31
32
enum Mode {
33
ModeNormal,
34
ModeBsxSlotted,
35
ModeBsx,
36
ModeSufamiTurbo,
37
ModeSuperGameBoy,
38
};
39
40
enum Type {
41
TypeNormal,
42
TypeBsxSlotted,
43
TypeBsxBios,
44
TypeBsx,
45
TypeSufamiTurboBios,
46
TypeSufamiTurbo,
47
TypeSuperGameBoy1Bios,
48
TypeSuperGameBoy2Bios,
49
TypeGameBoy,
50
TypeUnknown,
51
};
52
53
enum Region {
54
NTSC,
55
PAL,
56
};
57
58
enum MemoryMapper {
59
LoROM,
60
HiROM,
61
ExLoROM,
62
ExHiROM,
63
SuperFXROM,
64
SA1ROM,
65
SPC7110ROM,
66
BSCLoROM,
67
BSCHiROM,
68
BSXROM,
69
STROM,
70
};
71
72
enum DSP1MemoryMapper {
73
DSP1Unmapped,
74
DSP1LoROM1MB,
75
DSP1LoROM2MB,
76
DSP1HiROM,
77
};
78
79
bool loaded; //is a base cartridge inserted?
80
unsigned crc32; //crc32 of all cartridges (base+slot(s))
81
unsigned rom_size;
82
unsigned ram_size;
83
84
Mode mode;
85
Type type;
86
Region region;
87
MemoryMapper mapper;
88
DSP1MemoryMapper dsp1_mapper;
89
90
bool has_bsx_slot;
91
bool has_superfx;
92
bool has_sa1;
93
bool has_srtc;
94
bool has_sdd1;
95
bool has_spc7110;
96
bool has_spc7110rtc;
97
bool has_cx4;
98
bool has_dsp1;
99
bool has_dsp2;
100
bool has_dsp3;
101
bool has_dsp4;
102
bool has_obc1;
103
bool has_st010;
104
bool has_st011;
105
bool has_st018;
106
};
107
108
SnesCartridge::SnesCartridge(const uint8_t *data, unsigned size) {
109
read_header(data, size);
110
111
string xml;
112
markup = "<?xml version='1.0' encoding='UTF-8'?>\n";
113
114
if(type == TypeBsx) {
115
markup.append("<cartridge/>\n");
116
return;
117
}
118
119
if(type == TypeSufamiTurbo) {
120
markup.append("<cartridge/>\n");
121
return;
122
}
123
124
if(type == TypeGameBoy) {
125
markup.append("<cartridge rtc='", gameboy_has_rtc(data, size), "'\n");
126
if(gameboy_ram_size(data, size) > 0) {
127
markup.append(" <ram size='0x", hex(gameboy_ram_size(data, size)), "'>\n");
128
}
129
markup.append("</cartridge>\n");
130
return;
131
}
132
133
const char *range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? "0000-7fff" : "0000-ffff";
134
markup.append("<cartridge region='", region == NTSC ? "NTSC" : "PAL", "'>\n");
135
136
if(type == TypeSuperGameBoy1Bios || type == TypeSuperGameBoy2Bios) markup.append(
137
" <rom>\n"
138
" <map mode='linear' address='00-7f:8000-ffff'/>\n"
139
" <map mode='linear' address='80-ff:8000-ffff'/>\n"
140
" </rom>\n"
141
" <icd2 revision='1'>\n"
142
" <map address='00-3f:6000-7fff'/>\n"
143
" <map address='80-bf:6000-7fff'/>\n"
144
" </icd2>\n"
145
);
146
147
else if(has_cx4) markup.append(
148
" <hitachidsp model='HG51B169' frequency='20000000' firmware='cx4.rom' sha256='ae8d4d1961b93421ff00b3caa1d0f0ce7783e749772a3369c36b3dbf0d37ef18'>\n"
149
" <rom>\n"
150
" <map mode='linear' address='00-7f:8000-ffff'/>\n"
151
" <map mode='linear' address='80-ff:8000-ffff'/>\n"
152
" </rom>\n"
153
" <mmio>\n"
154
" <map address='00-3f:6000-7fff'/>\n"
155
" <map address='80-bf:6000-7fff'/>\n"
156
" </mmio>\n"
157
" </hitachidsp>\n"
158
);
159
160
else if(has_spc7110) {
161
markup.append(
162
" <rom>\n"
163
" <map mode='shadow' address='00-0f:8000-ffff'/>\n"
164
" <map mode='shadow' address='80-bf:8000-ffff'/>\n"
165
" <map mode='linear' address='c0-cf:0000-ffff'/>\n"
166
" </rom>\n"
167
" <spc7110>\n"
168
" <ram size='0x", hex(ram_size), "'>\n"
169
" <map mode='linear' address='00:6000-7fff'/>\n"
170
" <map mode='linear' address='30:6000-7fff'/>\n"
171
" </ram>\n"
172
" <mmio>\n"
173
" <map address='00-3f:4800-483f'/>\n"
174
" <map address='80-bf:4800-483f'/>\n"
175
" </mmio>\n"
176
" <mcu>\n"
177
" <map address='d0-ff:0000-ffff' offset='0x100000' size='0x", hex(size - 0x100000), "'/>\n"
178
" </mcu>\n"
179
" <dcu>\n"
180
" <map address='50:0000-ffff'/>\n"
181
" </dcu>\n"
182
);
183
if(has_spc7110rtc) markup.append(
184
" <rtc>\n"
185
" <map address='00-3f:4840-4842'/>\n"
186
" <map address='80-bf:4840-4842'/>\n"
187
" </rtc>\n"
188
);
189
markup.append(
190
" </spc7110>\n"
191
);
192
}
193
194
else if(mapper == LoROM) {
195
markup.append(
196
" <rom>\n"
197
" <map mode='linear' address='00-7f:8000-ffff'/>\n"
198
" <map mode='linear' address='80-ff:8000-ffff'/>\n"
199
" </rom>\n"
200
);
201
if(ram_size > 0) markup.append(
202
" <ram size='0x", hex(ram_size), "'>\n"
203
" <map mode='linear' address='20-3f:6000-7fff'/>\n"
204
" <map mode='linear' address='a0-bf:6000-7fff'/>\n"
205
" <map mode='linear' address='70-7f:", range, "'/>\n"
206
" <map mode='linear' address='f0-ff:", range, "'/>\n"
207
" </ram>\n"
208
);
209
}
210
211
else if(mapper == HiROM) {
212
markup.append(
213
" <rom>\n"
214
" <map mode='shadow' address='00-3f:8000-ffff'/>\n"
215
" <map mode='linear' address='40-7f:0000-ffff'/>\n"
216
" <map mode='shadow' address='80-bf:8000-ffff'/>\n"
217
" <map mode='linear' address='c0-ff:0000-ffff'/>\n"
218
" </rom>\n"
219
);
220
if(ram_size > 0) markup.append(
221
" <ram size='0x", hex(ram_size), "'>\n"
222
" <map mode='linear' address='20-3f:6000-7fff'/>\n"
223
" <map mode='linear' address='a0-bf:6000-7fff'/>\n"
224
" <map mode='linear' address='70-7f:", range, "'/>\n"
225
" </ram>\n"
226
);
227
}
228
229
else if(mapper == ExLoROM) {
230
markup.append(
231
" <rom>\n"
232
" <map mode='linear' address='00-3f:8000-ffff'/>\n"
233
" <map mode='linear' address='40-7f:0000-ffff'/>\n"
234
" <map mode='linear' address='80-bf:8000-ffff'/>\n"
235
" </rom>\n"
236
);
237
if(ram_size > 0) markup.append(
238
" <ram size='0x", hex(ram_size), "'>\n"
239
" <map mode='linear' address='20-3f:6000-7fff'/>\n"
240
" <map mode='linear' address='a0-bf:6000-7fff'/>\n"
241
" <map mode='linear' address='70-7f:0000-7fff'/>\n"
242
" </ram>\n"
243
);
244
}
245
246
else if(mapper == ExHiROM) {
247
markup.append(
248
" <rom>\n"
249
" <map mode='shadow' address='00-3f:8000-ffff' offset='0x400000'/>\n"
250
" <map mode='linear' address='40-7f:0000-ffff' offset='0x400000'/>\n"
251
" <map mode='shadow' address='80-bf:8000-ffff' offset='0x000000'/>\n"
252
" <map mode='linear' address='c0-ff:0000-ffff' offset='0x000000'/>\n"
253
" </rom>\n"
254
);
255
if(ram_size > 0) markup.append(
256
" <ram size='0x", hex(ram_size), "'>\n"
257
" <map mode='linear' address='20-3f:6000-7fff'/>\n"
258
" <map mode='linear' address='a0-bf:6000-7fff'/>\n"
259
" <map mode='linear' address='70-7f:", range, "'/>\n"
260
" </ram>\n"
261
);
262
}
263
264
else if(mapper == SuperFXROM) markup.append(
265
" <superfx revision='2'>\n"
266
" <rom>\n"
267
" <map mode='linear' address='00-3f:8000-ffff'/>\n"
268
" <map mode='linear' address='40-5f:0000-ffff'/>\n"
269
" <map mode='linear' address='80-bf:8000-ffff'/>\n"
270
" <map mode='linear' address='c0-df:0000-ffff'/>\n"
271
" </rom>\n"
272
" <ram size='0x", hex(ram_size), "'>\n"
273
" <map mode='linear' address='00-3f:6000-7fff' size='0x2000'/>\n"
274
" <map mode='linear' address='60-7f:0000-ffff'/>\n"
275
" <map mode='linear' address='80-bf:6000-7fff' size='0x2000'/>\n"
276
" <map mode='linear' address='e0-ff:0000-ffff'/>\n"
277
" </ram>\n"
278
" <mmio>\n"
279
" <map address='00-3f:3000-32ff'/>\n"
280
" <map address='80-bf:3000-32ff'/>\n"
281
" </mmio>\n"
282
" </superfx>\n"
283
);
284
285
else if(mapper == SA1ROM) markup.append(
286
" <sa1>\n"
287
" <mcu>\n"
288
" <rom>\n"
289
" <map mode='direct' address='00-3f:8000-ffff'/>\n"
290
" <map mode='direct' address='80-bf:8000-ffff'/>\n"
291
" <map mode='direct' address='c0-ff:0000-ffff'/>\n"
292
" </rom>\n"
293
" <ram>\n"
294
" <map mode='direct' address='00-3f:6000-7fff'/>\n"
295
" <map mode='direct' address='80-bf:6000-7fff'/>\n"
296
" </ram>\n"
297
" </mcu>\n"
298
" <iram size='0x800'>\n"
299
" <map mode='linear' address='00-3f:3000-37ff'/>\n"
300
" <map mode='linear' address='80-bf:3000-37ff'/>\n"
301
" </iram>\n"
302
" <bwram size='0x", hex(ram_size), "'>\n"
303
" <map mode='linear' address='40-4f:0000-ffff'/>\n"
304
" </bwram>\n"
305
" <mmio>\n"
306
" <map address='00-3f:2200-23ff'/>\n"
307
" <map address='80-bf:2200-23ff'/>\n"
308
" </mmio>\n"
309
" </sa1>\n"
310
);
311
312
else if(mapper == BSCLoROM) markup.append(
313
" <rom>\n"
314
" <map mode='linear' address='00-1f:8000-ffff' offset='0x000000'/>\n"
315
" <map mode='linear' address='20-3f:8000-ffff' offset='0x100000'/>\n"
316
" <map mode='linear' address='80-9f:8000-ffff' offset='0x200000'/>\n"
317
" <map mode='linear' address='a0-bf:8000-ffff' offset='0x100000'/>\n"
318
" </rom>\n"
319
" <ram size='0x", hex(ram_size), "'>\n"
320
" <map mode='linear' address='70-7f:0000-7fff'/>\n"
321
" <map mode='linear' address='f0-ff:0000-7fff'/>\n"
322
" </ram>\n"
323
" <bsx>\n"
324
" <slot>\n"
325
" <map mode='linear' address='c0-ef:0000-ffff'/>\n"
326
" </slot>\n"
327
" </bsx>\n"
328
);
329
330
else if(mapper == BSCHiROM) markup.append(
331
" <rom>\n"
332
" <map mode='shadow' address='00-1f:8000-ffff'/>\n"
333
" <map mode='linear' address='40-5f:0000-ffff'/>\n"
334
" <map mode='shadow' address='80-9f:8000-ffff'/>\n"
335
" <map mode='linear' address='c0-df:0000-ffff'/>\n"
336
" </rom>\n"
337
" <ram size='0x", hex(ram_size), "'>\n"
338
" <map mode='linear' address='20-3f:6000-7fff'/>\n"
339
" <map mode='linear' address='a0-bf:6000-7fff'/>\n"
340
" </ram>\n"
341
" <bsx>\n"
342
" <slot>\n"
343
" <map mode='shadow' address='20-3f:8000-ffff'/>\n"
344
" <map mode='linear' address='60-7f:0000-ffff'/>\n"
345
" <map mode='shadow' address='a0-bf:8000-ffff'/>\n"
346
" <map mode='linear' address='e0-ff:0000-ffff'/>\n"
347
" </slot>\n"
348
" </bsx>\n"
349
);
350
351
else if(mapper == BSXROM) markup.append(
352
" <bsx>\n"
353
" <mcu>\n"
354
" <map address='00-3f:8000-ffff'/>\n"
355
" <map address='80-bf:8000-ffff'/>\n"
356
" <map address='40-7f:0000-ffff'/>\n"
357
" <map address='c0-ff:0000-ffff'/>\n"
358
" <map address='20-3f:6000-7fff'/>\n"
359
" </mcu>\n"
360
" <mmio>\n"
361
" <map address='00-3f:5000-5fff'/>\n"
362
" <map address='80-bf:5000-5fff'/>\n"
363
" </mmio>\n"
364
" </bsx>\n"
365
);
366
367
else if(mapper == STROM) markup.append(
368
" <rom>\n"
369
" <map mode='linear' address='00-1f:8000-ffff'/>\n"
370
" <map mode='linear' address='80-9f:8000-ffff'/>\n"
371
" </rom>\n"
372
" <sufamiturbo>\n"
373
" <slot id='A'>\n"
374
" <rom>\n"
375
" <map mode='linear' address='20-3f:8000-ffff'/>\n"
376
" <map mode='linear' address='a0-bf:8000-ffff'/>\n"
377
" </rom>\n"
378
" <ram size='0x20000'>\n"
379
" <map mode='linear' address='60-63:8000-ffff'/>\n"
380
" <map mode='linear' address='e0-e3:8000-ffff'/>\n"
381
" </ram>\n"
382
" </slot>\n"
383
" <slot id='B'>\n"
384
" <rom>\n"
385
" <map mode='linear' address='40-5f:8000-ffff'/>\n"
386
" <map mode='linear' address='c0-df:8000-ffff'/>\n"
387
" </rom>\n"
388
" <ram size='0x20000'>\n"
389
" <map mode='linear' address='70-73:8000-ffff'/>\n"
390
" <map mode='linear' address='f0-f3:8000-ffff'/>\n"
391
" </ram>\n"
392
" </slot>\n"
393
" </sufamiturbo>\n"
394
);
395
396
if(has_srtc) markup.append(
397
" <srtc>\n"
398
" <map address='00-3f:2800-2801'/>\n"
399
" <map address='80-bf:2800-2801'/>\n"
400
" </srtc>\n"
401
);
402
403
if(has_sdd1) markup.append(
404
" <sdd1>\n"
405
" <mcu>\n"
406
" <map address='c0-ff:0000-ffff'/>\n"
407
" </mcu>\n"
408
" <mmio>\n"
409
" <map address='00-3f:4800-4807'/>\n"
410
" <map address='80-bf:4800-4807'/>\n"
411
" </mmio>\n"
412
" </sdd1>\n"
413
);
414
415
if(has_obc1) markup.append(
416
" <obc1>\n"
417
" <map address='00-3f:6000-7fff'/>\n"
418
" <map address='80-bf:6000-7fff'/>\n"
419
" </obc1>\n"
420
);
421
422
if(has_dsp1) {
423
//91e87d11e1c30d172556bed2211cce2efa94ba595f58c5d264809ef4d363a97b dsp1.rom
424
markup.append(" <necdsp model='uPD7725' frequency='8000000' firmware='dsp1b.rom' sha256='d789cb3c36b05c0b23b6c6f23be7aa37c6e78b6ee9ceac8d2d2aa9d8c4d35fa9'>\n");
425
if(dsp1_mapper == DSP1LoROM1MB) markup.append(
426
" <dr>\n"
427
" <map address='20-3f:8000-bfff'/>\n"
428
" <map address='a0-bf:8000-bfff'/>\n"
429
" </dr>\n"
430
" <sr>\n"
431
" <map address='20-3f:c000-ffff'/>\n"
432
" <map address='a0-bf:c000-ffff'/>\n"
433
" </sr>\n"
434
);
435
if(dsp1_mapper == DSP1LoROM2MB) markup.append(
436
" <dr>\n"
437
" <map address='60-6f:0000-3fff'/>\n"
438
" <map address='e0-ef:0000-3fff'/>\n"
439
" </dr>\n"
440
" <sr>\n"
441
" <map address='60-6f:4000-7fff'/>\n"
442
" <map address='e0-ef:4000-7fff'/>\n"
443
" </sr>\n"
444
);
445
if(dsp1_mapper == DSP1HiROM) markup.append(
446
" <dr>\n"
447
" <map address='00-1f:6000-6fff'/>\n"
448
" <map address='80-9f:6000-6fff'/>\n"
449
" </dr>\n"
450
" <sr>\n"
451
" <map address='00-1f:7000-7fff'/>\n"
452
" <map address='80-9f:7000-7fff'/>\n"
453
" </sr>\n"
454
);
455
markup.append(" </necdsp>\n");
456
}
457
458
if(has_dsp2) markup.append(
459
" <necdsp model='uPD7725' frequency='8000000' firmware='dsp2.rom' sha256='03ef4ef26c9f701346708cb5d07847b5203cf1b0818bf2930acd34510ffdd717'>\n"
460
" <dr>\n"
461
" <map address='20-3f:8000-bfff'/>\n"
462
" <map address='a0-bf:8000-bfff'/>\n"
463
" </dr>\n"
464
" <sr>\n"
465
" <map address='20-3f:c000-ffff'/>\n"
466
" <map address='a0-bf:c000-ffff'/>\n"
467
" </sr>\n"
468
" </necdsp>\n"
469
);
470
471
if(has_dsp3) markup.append(
472
" <necdsp model='uPD7725' frequency='8000000' firmware='dsp3.rom' sha256='0971b08f396c32e61989d1067dddf8e4b14649d548b2188f7c541b03d7c69e4e'>\n"
473
" <dr>\n"
474
" <map address='20-3f:8000-bfff'/>\n"
475
" <map address='a0-bf:8000-bfff'/>\n"
476
" </dr>\n"
477
" <sr>\n"
478
" <map address='20-3f:c000-ffff'/>\n"
479
" <map address='a0-bf:c000-ffff'/>\n"
480
" </sr>\n"
481
" </necdsp>\n"
482
);
483
484
if(has_dsp4) markup.append(
485
" <necdsp model='uPD7725' frequency='8000000' firmware='dsp4.rom' sha256='752d03b2d74441e430b7f713001fa241f8bbcfc1a0d890ed4143f174dbe031da'>\n"
486
" <dr>\n"
487
" <map address='30-3f:8000-bfff'/>\n"
488
" <map address='b0-bf:8000-bfff'/>\n"
489
" </dr>\n"
490
" <sr>\n"
491
" <map address='30-3f:c000-ffff'/>\n"
492
" <map address='b0-bf:c000-ffff'/>\n"
493
" </sr>\n"
494
" </necdsp>\n"
495
);
496
497
if(has_st010) markup.append(
498
" <necdsp model='uPD96050' frequency='10000000' firmware='st010.rom' sha256='fa9bced838fedea11c6f6ace33d1878024bdd0d02cc9485899d0bdd4015ec24c'>\n"
499
" <dr>\n"
500
" <map address='60:0000'/>\n"
501
" <map address='e0:0000'/>\n"
502
" </dr>\n"
503
" <sr>\n"
504
" <map address='60:0001'/>\n"
505
" <map address='e0:0001'/>\n"
506
" </sr>\n"
507
" <dp>\n"
508
" <map address='68-6f:0000-0fff'/>\n"
509
" <map address='e8-ef:0000-0fff'/>\n"
510
" </dp>\n"
511
" </necdsp>\n"
512
);
513
514
if(has_st011) markup.append(
515
" <necdsp model='uPD96050' frequency='15000000' firmware='st011.rom' sha256='8b2b3f3f3e6e29f4d21d8bc736b400bc988b7d2214ebee15643f01c1fee2f364'>\n"
516
" <dr>\n"
517
" <map address='60:0000'/>\n"
518
" <map address='e0:0000'/>\n"
519
" </dr>\n"
520
" <sr>\n"
521
" <map address='60:0001'/>\n"
522
" <map address='e0:0001'/>\n"
523
" </sr>\n"
524
" <dp>\n"
525
" <map address='68-6f:0000-0fff'/>\n"
526
" <map address='e8-ef:0000-0fff'/>\n"
527
" </dp>\n"
528
" </necdsp>\n"
529
);
530
531
if(has_st018) markup.append(
532
" <armdsp firmware='st018.rom' frequency='21477272' sha256='6df209ab5d2524d1839c038be400ae5eb20dafc14a3771a3239cd9e8acd53806'>\n"
533
" <map address='00-3f:3800-38ff'/>\n"
534
" <map address='80-bf:3800-38ff'/>\n"
535
" </armdsp>\n"
536
);
537
538
markup.append("</cartridge>\n");
539
}
540
541
void SnesCartridge::read_header(const uint8_t *data, unsigned size) {
542
type = TypeUnknown;
543
mapper = LoROM;
544
dsp1_mapper = DSP1Unmapped;
545
region = NTSC;
546
rom_size = size;
547
ram_size = 0;
548
549
has_bsx_slot = false;
550
has_superfx = false;
551
has_sa1 = false;
552
has_srtc = false;
553
has_sdd1 = false;
554
has_spc7110 = false;
555
has_spc7110rtc = false;
556
has_cx4 = false;
557
has_dsp1 = false;
558
has_dsp2 = false;
559
has_dsp3 = false;
560
has_dsp4 = false;
561
has_obc1 = false;
562
has_st010 = false;
563
has_st011 = false;
564
has_st018 = false;
565
566
//=====================
567
//detect Game Boy carts
568
//=====================
569
570
if(size >= 0x0140) {
571
if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66
572
&& data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) {
573
type = TypeGameBoy;
574
return;
575
}
576
}
577
578
if(size < 32768) {
579
type = TypeUnknown;
580
return;
581
}
582
583
const unsigned index = find_header(data, size);
584
const uint8_t mapperid = data[index + Mapper];
585
const uint8_t rom_type = data[index + RomType];
586
const uint8_t rom_size = data[index + RomSize];
587
const uint8_t company = data[index + Company];
588
const uint8_t regionid = data[index + CartRegion] & 0x7f;
589
590
ram_size = 1024 << (data[index + RamSize] & 7);
591
if(ram_size == 1024) ram_size = 0; //no RAM present
592
593
//0, 1, 13 = NTSC; 2 - 12 = PAL
594
region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL;
595
596
//=======================
597
//detect BS-X flash carts
598
//=======================
599
600
if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) {
601
if(data[index + 0x14] == 0x00) {
602
const uint8_t n15 = data[index + 0x15];
603
if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) {
604
if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) {
605
type = TypeBsx;
606
mapper = BSXROM;
607
region = NTSC; //BS-X only released in Japan
608
return;
609
}
610
}
611
}
612
}
613
614
//=========================
615
//detect Sufami Turbo carts
616
//=========================
617
618
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
619
if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) {
620
type = TypeSufamiTurboBios;
621
} else {
622
type = TypeSufamiTurbo;
623
}
624
mapper = STROM;
625
region = NTSC; //Sufami Turbo only released in Japan
626
return; //RAM size handled outside this routine
627
}
628
629
//==========================
630
//detect Super Game Boy BIOS
631
//==========================
632
633
if(!memcmp(data + index, "Super GAMEBOY2", 14)) {
634
type = TypeSuperGameBoy2Bios;
635
return;
636
}
637
638
if(!memcmp(data + index, "Super GAMEBOY", 13)) {
639
type = TypeSuperGameBoy1Bios;
640
return;
641
}
642
643
//=====================
644
//detect standard carts
645
//=====================
646
647
//detect presence of BS-X flash cartridge connector (reads extended header information)
648
if(data[index - 14] == 'Z') {
649
if(data[index - 11] == 'J') {
650
uint8_t n13 = data[index - 13];
651
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
652
if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) {
653
has_bsx_slot = true;
654
}
655
}
656
}
657
}
658
659
if(has_bsx_slot) {
660
if(!memcmp(data + index, "Satellaview BS-X ", 21)) {
661
//BS-X base cart
662
type = TypeBsxBios;
663
mapper = BSXROM;
664
region = NTSC; //BS-X only released in Japan
665
return; //RAM size handled internally by load_cart_bsx() -> BSXCart class
666
} else {
667
type = TypeBsxSlotted;
668
mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
669
region = NTSC; //BS-X slotted cartridges only released in Japan
670
}
671
} else {
672
//standard cart
673
type = TypeNormal;
674
675
if(index == 0x7fc0 && size >= 0x401000) {
676
mapper = ExLoROM;
677
} else if(index == 0x7fc0 && mapperid == 0x32) {
678
mapper = ExLoROM;
679
} else if(index == 0x7fc0) {
680
mapper = LoROM;
681
} else if(index == 0xffc0) {
682
mapper = HiROM;
683
} else { //index == 0x40ffc0
684
mapper = ExHiROM;
685
}
686
}
687
688
if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) {
689
has_superfx = true;
690
mapper = SuperFXROM;
691
ram_size = 1024 << (data[index - 3] & 7);
692
if(ram_size == 1024) ram_size = 0;
693
}
694
695
if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) {
696
has_sa1 = true;
697
mapper = SA1ROM;
698
}
699
700
if(mapperid == 0x35 && rom_type == 0x55) {
701
has_srtc = true;
702
}
703
704
if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) {
705
has_sdd1 = true;
706
}
707
708
if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) {
709
has_spc7110 = true;
710
has_spc7110rtc = (rom_type == 0xf9);
711
mapper = SPC7110ROM;
712
}
713
714
if(mapperid == 0x20 && rom_type == 0xf3) {
715
has_cx4 = true;
716
}
717
718
if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) {
719
has_dsp1 = true;
720
}
721
722
if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) {
723
has_dsp1 = true;
724
}
725
726
if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) {
727
has_dsp1 = true;
728
}
729
730
if(has_dsp1 == true) {
731
if((mapperid & 0x2f) == 0x20 && size <= 0x100000) {
732
dsp1_mapper = DSP1LoROM1MB;
733
} else if((mapperid & 0x2f) == 0x20) {
734
dsp1_mapper = DSP1LoROM2MB;
735
} else if((mapperid & 0x2f) == 0x21) {
736
dsp1_mapper = DSP1HiROM;
737
}
738
}
739
740
if(mapperid == 0x20 && rom_type == 0x05) {
741
has_dsp2 = true;
742
}
743
744
if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) {
745
has_dsp3 = true;
746
}
747
748
if(mapperid == 0x30 && rom_type == 0x03) {
749
has_dsp4 = true;
750
}
751
752
if(mapperid == 0x30 && rom_type == 0x25) {
753
has_obc1 = true;
754
}
755
756
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) {
757
has_st010 = true;
758
}
759
760
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) {
761
has_st011 = true;
762
}
763
764
if(mapperid == 0x30 && rom_type == 0xf5) {
765
has_st018 = true;
766
}
767
}
768
769
unsigned SnesCartridge::find_header(const uint8_t *data, unsigned size) {
770
unsigned score_lo = score_header(data, size, 0x007fc0);
771
unsigned score_hi = score_header(data, size, 0x00ffc0);
772
unsigned score_ex = score_header(data, size, 0x40ffc0);
773
if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits
774
775
if(score_lo >= score_hi && score_lo >= score_ex) {
776
return 0x007fc0;
777
} else if(score_hi >= score_ex) {
778
return 0x00ffc0;
779
} else {
780
return 0x40ffc0;
781
}
782
}
783
784
unsigned SnesCartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) {
785
if(size < addr + 64) return 0; //image too small to contain header at this location?
786
int score = 0;
787
788
uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8);
789
uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8);
790
uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8);
791
792
uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset
793
uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit
794
795
//$00:[000-7fff] contains uninitialized RAM and MMIO.
796
//reset vector must point to ROM at $00:[8000-ffff] to be considered valid.
797
if(resetvector < 0x8000) return 0;
798
799
//some images duplicate the header in multiple locations, and others have completely
800
//invalid header information that cannot be relied upon.
801
//below code will analyze the first opcode executed at the specified reset vector to
802
//determine the probability that this is the correct header.
803
804
//most likely opcodes
805
if(resetop == 0x78 //sei
806
|| resetop == 0x18 //clc (clc; xce)
807
|| resetop == 0x38 //sec (sec; xce)
808
|| resetop == 0x9c //stz $nnnn (stz $4200)
809
|| resetop == 0x4c //jmp $nnnn
810
|| resetop == 0x5c //jml $nnnnnn
811
) score += 8;
812
813
//plausible opcodes
814
if(resetop == 0xc2 //rep #$nn
815
|| resetop == 0xe2 //sep #$nn
816
|| resetop == 0xad //lda $nnnn
817
|| resetop == 0xae //ldx $nnnn
818
|| resetop == 0xac //ldy $nnnn
819
|| resetop == 0xaf //lda $nnnnnn
820
|| resetop == 0xa9 //lda #$nn
821
|| resetop == 0xa2 //ldx #$nn
822
|| resetop == 0xa0 //ldy #$nn
823
|| resetop == 0x20 //jsr $nnnn
824
|| resetop == 0x22 //jsl $nnnnnn
825
) score += 4;
826
827
//implausible opcodes
828
if(resetop == 0x40 //rti
829
|| resetop == 0x60 //rts
830
|| resetop == 0x6b //rtl
831
|| resetop == 0xcd //cmp $nnnn
832
|| resetop == 0xec //cpx $nnnn
833
|| resetop == 0xcc //cpy $nnnn
834
) score -= 4;
835
836
//least likely opcodes
837
if(resetop == 0x00 //brk #$nn
838
|| resetop == 0x02 //cop #$nn
839
|| resetop == 0xdb //stp
840
|| resetop == 0x42 //wdm
841
|| resetop == 0xff //sbc $nnnnnn,x
842
) score -= 8;
843
844
//at times, both the header and reset vector's first opcode will match ...
845
//fallback and rely on info validity in these cases to determine more likely header.
846
847
//a valid checksum is the biggest indicator of a valid header.
848
if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4;
849
850
if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM
851
if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM
852
if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM
853
if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM
854
855
if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header
856
if(data[addr + RomType] < 0x08) score++;
857
if(data[addr + RomSize] < 0x10) score++;
858
if(data[addr + RamSize] < 0x08) score++;
859
if(data[addr + CartRegion] < 14) score++;
860
861
if(score < 0) score = 0;
862
return score;
863
}
864
865
unsigned SnesCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) {
866
if(size < 512) return 0;
867
switch(data[0x0149]) {
868
case 0x00: return 0 * 1024;
869
case 0x01: return 8 * 1024;
870
case 0x02: return 8 * 1024;
871
case 0x03: return 32 * 1024;
872
case 0x04: return 128 * 1024;
873
case 0x05: return 128 * 1024;
874
default: return 128 * 1024;
875
}
876
}
877
878
bool SnesCartridge::gameboy_has_rtc(const uint8_t *data, unsigned size) {
879
if(size < 512) return false;
880
if(data[0x0147] == 0x0f ||data[0x0147] == 0x10) return true;
881
return false;
882
}
883
884
}
885
886
#endif
887
888