mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-04-02 10:42:14 -04:00
byuu says: Changelog: - md/vdp: added VIP bit to status register; fixes Cliffhanger - processor/m68k/disassembler: added modes 7 and 8 to LEA address disassembly - processor/m68k/disassembler: enhanced ILLEGAL to display LINEA/LINEF $xxx variants - processor/m68k: ILLEGAL/LINEA/LINEF do not modify the stack register; fixes Caeser no Yabou II - icarus/sfc: request sgb1.boot.rom and sgb2.boot.rom separately; as they are different - icarus/sfc: removed support for external firmware when loading ROM images The hack to run Mega Drive Ballz 3D isn't in place, as I don't know if it's correct, and the graphics were corrupted anyway. The SGB boot ROM change is going to require updating the icarus database as well. I will add that in when I start dumping more cartridges here soon. Finally ... I explained this already, but I'll do so here as well: I removed icarus' support for loading SNES coprocessor firmware games with external firmware files (eg dsp1.program.rom + dsp1.data.rom in the same path as supermariokart.sfc, for example.) I realize most are going to see this as an antagonizing/stubborn move given the recent No-Intro discussion, and I won't deny that said thread is why this came to the forefront of my mind. But on my word, I honestly believe this was an ineffective solution for many reasons not related to our disagreements: 1. No-Intro distributes SNES coprocessor firmware as a merged file, eg "DSP1 (World).zip/DSP1 (World).bin" -- icarus can't possibly know about every ROM distribution set's naming conventions for firmware. (Right now, it appears GoodSNES and NSRT are mostly dead; but there may be more DATs in the future -- including my own.) 2. Even if the user obtains the firmware and tries to rename it, it won't work: icarus parses manifests generated by the heuristics module and sees two ROM files: dsp1.program.rom and dsp1.data.rom. icarus cannot identify a file named dsp1.rom as containing both of these sub-files. Users are going to have to know how to split files, which there is no way to do on stock Windows. Merging files, however, can be done via `copy /b supermariokart.sfc+dsp1.rom supermariokartdsp.sfc`; - and dsp1.rom can be named whatever now. I am not saying this will be easy for the average user, but it's easier than splitting files. 3. Separate firmware breaks icarus' database lookup. If you have pilotwings.sfc but without firmware, icarus will not find a match for it in the database lookup phase. It will then fall back on heuristics. The heuristics will pick DSP1B for compatibility with Ballz 3D which requires it. And so it will try to pull in the wrong firmware, and the game's intro will not work correctly. Furthermore, the database information will be unavailable, resulting in inaccurate mirroring. So for these reasons, I have removed said support. You must now load SNES coprocessor games into higan in one of two ways: 1) game paks with split files; or 2) SFC images with merged firmware. If and when No-Intro deploys a method I can actually use, I give you all my word I will give it a fair shot and if it's reasonable, I'll support it in icarus.
856 lines
26 KiB
C++
856 lines
26 KiB
C++
struct SuperFamicomCartridge {
|
|
SuperFamicomCartridge(const uint8_t* data, uint size, bool has_msu1 = false);
|
|
|
|
string markup;
|
|
|
|
//private:
|
|
auto readHeader(const uint8_t* data, uint size) -> void;
|
|
auto findHeader(const uint8_t* data, uint size) -> uint;
|
|
auto scoreHeader(const uint8_t* data, uint size, uint addr) -> uint;
|
|
|
|
enum HeaderField : uint {
|
|
CartName = 0x00,
|
|
Mapper = 0x15,
|
|
RomType = 0x16,
|
|
RomSize = 0x17,
|
|
RamSize = 0x18,
|
|
CartRegion = 0x19,
|
|
Company = 0x1a,
|
|
Version = 0x1b,
|
|
Complement = 0x1c, //inverse checksum
|
|
Checksum = 0x1e,
|
|
ResetVector = 0x3c,
|
|
};
|
|
|
|
enum class Type : uint {
|
|
SatellaviewBIOS,
|
|
SufamiTurboBIOS,
|
|
SuperGameBoy1BIOS,
|
|
SuperGameBoy2BIOS,
|
|
LoROM,
|
|
HiROM,
|
|
ExLoROM,
|
|
ExHiROM,
|
|
SuperFX,
|
|
SA1,
|
|
LoROMSatellaview,
|
|
HiROMSatellaview,
|
|
CampusChallenge92,
|
|
Powerfest94,
|
|
|
|
//invalid types
|
|
Unknown,
|
|
GameBoy,
|
|
Satellaview,
|
|
SufamiTurbo,
|
|
};
|
|
|
|
enum class Region : uint {
|
|
NTSC,
|
|
PAL,
|
|
};
|
|
|
|
enum class DSP1Type : uint {
|
|
None,
|
|
LoROM1MB,
|
|
LoROM2MB,
|
|
HiROM,
|
|
};
|
|
|
|
bool loaded = false; //is a base cartridge inserted?
|
|
uint crc32 = 0; //crc32 of all cartridges (base+slot(s))
|
|
uint rom_size = 0;
|
|
uint ram_size = 0;
|
|
uint firmware_size = 0;
|
|
string firmware_missing;
|
|
|
|
Type type = Type::Unknown;
|
|
Region region = Region::NTSC;
|
|
DSP1Type dsp1_type = DSP1Type::None;
|
|
|
|
bool has_bsx_slot = false;
|
|
bool has_superfx = false;
|
|
bool has_sa1 = false;
|
|
bool has_sharprtc = false;
|
|
bool has_epsonrtc = false;
|
|
bool has_sdd1 = false;
|
|
bool has_spc7110 = false;
|
|
bool has_cx4 = false;
|
|
bool has_dsp1 = false;
|
|
bool has_dsp2 = false;
|
|
bool has_dsp3 = false;
|
|
bool has_dsp4 = false;
|
|
bool has_obc1 = false;
|
|
bool has_st010 = false;
|
|
bool has_st011 = false;
|
|
bool has_st018 = false;
|
|
};
|
|
|
|
SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t* data, uint size, bool has_msu1) {
|
|
//skip copier header
|
|
if((size & 0x7fff) == 512) data += 512, size -= 512;
|
|
|
|
//ignore images too small to be valid
|
|
if(size < 0x8000) return;
|
|
|
|
readHeader(data, size);
|
|
|
|
if(type == Type::Unknown) return;
|
|
if(type == Type::GameBoy) return;
|
|
if(type == Type::Satellaview) return;
|
|
if(type == Type::SufamiTurbo) return;
|
|
|
|
const char* range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? "0000-7fff" : "0000-ffff";
|
|
markup.append("board region=", region == Region::NTSC ? "ntsc" : "pal", "\n");
|
|
|
|
//detect firmware
|
|
|
|
if(has_dsp1) {
|
|
if((size & 0x7fff) == 0x2000) {
|
|
firmware_size = 0x2000;
|
|
} else {
|
|
firmware_missing = "DSP1";
|
|
}
|
|
}
|
|
|
|
if(has_dsp2) {
|
|
if((size & 0x7fff) == 0x2000) {
|
|
firmware_size = 0x2000;
|
|
} else {
|
|
firmware_missing = "DSP2";
|
|
}
|
|
}
|
|
|
|
if(has_dsp3) {
|
|
if((size & 0x7fff) == 0x2000) {
|
|
firmware_size = 0x2000;
|
|
} else {
|
|
firmware_missing = "DSP3";
|
|
}
|
|
}
|
|
|
|
if(has_dsp4) {
|
|
if((size & 0x7fff) == 0x2000) {
|
|
firmware_size = 0x2000;
|
|
} else {
|
|
firmware_missing = "DSP4";
|
|
}
|
|
}
|
|
|
|
if(has_st010) {
|
|
if((size & 0xffff) == 0xd000) {
|
|
firmware_size = 0xd000;
|
|
} else {
|
|
firmware_missing = "ST010";
|
|
}
|
|
}
|
|
|
|
if(has_st011) {
|
|
if((size & 0xffff) == 0xd000) {
|
|
firmware_size = 0xd000;
|
|
} else {
|
|
firmware_missing = "ST011";
|
|
}
|
|
}
|
|
|
|
if(has_st018) {
|
|
if((size & 0x3ffff) == 0x28000) {
|
|
firmware_size = 0x28000;
|
|
} else {
|
|
firmware_missing = "ST018";
|
|
}
|
|
}
|
|
|
|
if(has_cx4) {
|
|
if((rom_size & 0x7fff) == 0xc00) {
|
|
firmware_size = 0xc00;
|
|
} else {
|
|
firmware_missing = "CX4";
|
|
}
|
|
}
|
|
|
|
if(type == Type::SuperGameBoy1BIOS) {
|
|
if((rom_size & 0x7fff) == 0x100) {
|
|
firmware_size = 0x100;
|
|
} else {
|
|
firmware_missing = "SGB1";
|
|
}
|
|
}
|
|
|
|
if(type == Type::SuperGameBoy2BIOS) {
|
|
if((rom_size & 0x7fff) == 0x100) {
|
|
firmware_size = 0x100;
|
|
} else {
|
|
firmware_missing = "SGB2";
|
|
}
|
|
}
|
|
|
|
rom_size -= firmware_size;
|
|
|
|
//end firmware detection
|
|
|
|
if(type == Type::SatellaviewBIOS) {
|
|
markup.append(
|
|
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
|
" map address=10-1f:5000-5fff mask=0xf000\n"
|
|
" mcc\n"
|
|
" map address=00-0f:5000\n"
|
|
" map=mcu address=00-3f,80-bf:8000-ffff mask=0x408000\n"
|
|
" map=mcu address=40-7d,c0-ff:0000-ffff\n"
|
|
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
|
" ram name=download.ram size=0x80000\n"
|
|
" map address=00-3f,80-bf:6000-7fff mask=0xe000\n"
|
|
" bsmemory\n"
|
|
);
|
|
}
|
|
|
|
else if(type == Type::SufamiTurboBIOS) {
|
|
markup.append(
|
|
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
|
" map address=00-1f,80-9f:8000-ffff mask=0x8000\n"
|
|
" sufamiturbo\n"
|
|
" rom\n"
|
|
" map address=20-3f,a0-bf:8000-ffff mask=0x8000\n"
|
|
" ram\n"
|
|
" map address=60-6f,e0-ef:0000-ffff\n"
|
|
" sufamiturbo\n"
|
|
" rom\n"
|
|
" map address=40-5f,c0-df:0000-7fff mask=0x8000\n"
|
|
" map address=40-5f,c0-df:8000-ffff mask=0x8000\n"
|
|
" ram\n"
|
|
" map address=70-7d,f0-ff:0000-ffff\n"
|
|
);
|
|
}
|
|
|
|
else if(type == Type::SuperGameBoy1BIOS) {
|
|
markup.append(
|
|
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
|
" map address=00-7d,80-ff:8000-ffff mask=0x8000\n"
|
|
" map address=40-7d,c0-ff:0000-7fff mask=0x8000\n"
|
|
" icd2 revision=1\n"
|
|
" map address=00-3f,80-bf:6000-67ff,7000-7fff\n"
|
|
" rom name=sgb1.boot.rom size=0x100\n"
|
|
);
|
|
}
|
|
|
|
else if(type == Type::SuperGameBoy2BIOS) {
|
|
markup.append(
|
|
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
|
" map address=00-7d,80-ff:8000-ffff mask=0x8000\n"
|
|
" map address=40-7d,c0-ff:0000-7fff mask=0x8000\n"
|
|
" icd2 revision=2\n"
|
|
" map address=00-3f,80-bf:6000-67ff,7000-7fff\n"
|
|
" rom name=sgb2.boot.rom size=0x100\n"
|
|
);
|
|
}
|
|
|
|
else if(has_cx4) {
|
|
markup.append(
|
|
" hitachidsp model=HG51B169 frequency=20000000\n"
|
|
" map address=00-3f,80-bf:6c00-6fff,7c00-7fff\n"
|
|
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
|
" map address=00-3f,80-bf:8000-ffff mask=0x8000\n"
|
|
" ram name=save.ram size=0\n"
|
|
" map address=70-77:0000-7fff mask=0x8000\n"
|
|
" drom name=cx4.data.rom size=0xc00\n"
|
|
" dram name=cx4.data.ram size=0xc00 volatile\n"
|
|
" map address=00-3f,80-bf:6000-6bff,7000-7bff mask=0xf000\n"
|
|
);
|
|
}
|
|
|
|
else if(has_spc7110) {
|
|
markup.append(
|
|
" spc7110\n"
|
|
" map address=00-3f,80-bf:4800-483f\n"
|
|
" map address=50,58:0000-ffff\n"
|
|
" map=mcu address=00-3f,80-bf:8000-ffff mask=0x800000\n"
|
|
" map=mcu address=c0-ff:0000-ffff mask=0xc00000\n"
|
|
" prom name=program.rom size=0x100000\n"
|
|
" drom name=data.rom size=0x", hex(rom_size - 0x100000), "\n"
|
|
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
|
" map address=00-3f,80-bf:6000-7fff mask=0xe000\n"
|
|
);
|
|
}
|
|
|
|
else if(has_sdd1) {
|
|
markup.append(
|
|
" sdd1\n"
|
|
" map address=00-3f,80-bf:4800-480f\n"
|
|
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
|
" map address=00-3f,80-bf:8000-ffff\n"
|
|
" map address=c0-ff:0000-ffff\n"
|
|
);
|
|
if(ram_size > 0) markup.append(
|
|
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
|
" map address=00-3f,80-bf:6000-7fff mask=0xe000\n"
|
|
" map address=70-73:0000-ffff mask=0x8000\n"
|
|
);
|
|
}
|
|
|
|
else if(type == Type::LoROM) {
|
|
markup.append(
|
|
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
|
" map address=00-7d,80-ff:8000-ffff mask=0x8000\n"
|
|
" map address=40-6f,c0-ef:0000-7fff mask=0x8000\n"
|
|
);
|
|
if(ram_size > 0) markup.append(
|
|
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
|
" map address=70-7d,f0-ff:", range, "\n"
|
|
);
|
|
}
|
|
|
|
else if(type == Type::HiROM) {
|
|
markup.append(
|
|
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
|
" map address=00-3f,80-bf:8000-ffff\n"
|
|
" map address=40-7f,c0-ff:0000-ffff\n"
|
|
);
|
|
if(ram_size > 0) markup.append(
|
|
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
|
" map address=10-3f,90-bf:6000-7fff mask=0xe000\n"
|
|
);
|
|
}
|
|
|
|
else if(type == Type::ExLoROM) {
|
|
markup.append(
|
|
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
|
" map address=00-3f,80-bf:8000-ffff mask=0x8000\n"
|
|
" map address=40-7d:0000-ffff\n"
|
|
);
|
|
if(ram_size > 0) markup.append(
|
|
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
|
" map address=20-3f,a0-bf:6000-7fff mask=0xe000\n"
|
|
" map address=70-7d:0000-7fff mask=0x8000\n"
|
|
);
|
|
}
|
|
|
|
else if(type == Type::ExHiROM) {
|
|
markup.append(
|
|
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
|
" map address=00-3f:8000-ffff base=0x400000\n"
|
|
" map address=40-7d:0000-ffff base=0x400000\n"
|
|
" map address=80-bf:8000-ffff mask=0xc00000\n"
|
|
" map address=c0-ff:0000-ffff mask=0xc00000\n"
|
|
);
|
|
if(ram_size > 0) markup.append(
|
|
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
|
" map address=20-3f,a0-bf:6000-7fff mask=0xe000\n"
|
|
" map address=70-7d:", range, "\n"
|
|
);
|
|
}
|
|
|
|
else if(type == Type::SuperFX) {
|
|
markup.append(
|
|
" superfx\n"
|
|
" map address=00-3f,80-bf:3000-34ff\n"
|
|
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
|
" map address=00-3f,80-bf:8000-ffff mask=0x8000\n"
|
|
" map address=40-5f,c0-df:0000-ffff\n"
|
|
);
|
|
if(ram_size > 0) markup.append(
|
|
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
|
" map address=00-3f,80-bf:6000-7fff size=0x2000\n"
|
|
" map address=70-71,f0-f1:0000-ffff\n"
|
|
);
|
|
}
|
|
|
|
else if(type == Type::SA1) {
|
|
markup.append(
|
|
" sa1\n"
|
|
" map address=00-3f,80-bf:2200-23ff\n"
|
|
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
|
" map address=00-3f,80-bf:8000-ffff mask=0x408000\n"
|
|
" map address=c0-ff:0000-ffff\n"
|
|
);
|
|
if(ram_size > 0) markup.append(
|
|
" bwram name=save.ram size=0x", hex(ram_size), "\n"
|
|
" map address=00-3f,80-bf:6000-7fff size=0x2000\n"
|
|
" map address=40-4f:0000-ffff\n"
|
|
);
|
|
markup.append(
|
|
" iram id=internal size=0x800 volatile\n"
|
|
" map address=00-3f,80-bf:3000-37ff size=0x800\n"
|
|
);
|
|
}
|
|
|
|
else if(type == Type::LoROMSatellaview) {
|
|
markup.append(
|
|
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
|
" map address=00-1f:8000-ffff base=0x000000 mask=0x8000\n"
|
|
" map address=20-3f:8000-ffff base=0x100000 mask=0x8000\n"
|
|
" map address=80-9f:8000-ffff base=0x200000 mask=0x8000\n"
|
|
" map address=a0-bf:8000-ffff base=0x100000 mask=0x8000\n"
|
|
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
|
" map address=70-7d,f0-ff:0000-7fff mask=0x8000\n"
|
|
" bsmemory\n"
|
|
" map address=c0-ef:0000-ffff\n"
|
|
);
|
|
}
|
|
|
|
else if(type == Type::HiROMSatellaview) {
|
|
markup.append(
|
|
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
|
" map address=00-1f,80-9f:8000-ffff\n"
|
|
" map address=40-5f,c0-df:0000-ffff\n"
|
|
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
|
" map address=20-3f,a0-bf:6000-7fff\n"
|
|
" bsmemory\n"
|
|
" map address=20-3f,a0-bf:8000-ffff\n"
|
|
" map address=60-7f,e0-ff:0000-ffff\n"
|
|
);
|
|
}
|
|
|
|
else if(type == Type::CampusChallenge92) {
|
|
markup.append(
|
|
" event=CC92 timer=360\n"
|
|
" map address=c0,e0:0000\n"
|
|
" map=mcu address=00-1f,80-9f:8000-ffff\n"
|
|
" rom name=program.rom size=0x40000\n"
|
|
" rom name=slot-1.rom size=0x80000\n"
|
|
" rom name=slot-2.rom size=0x80000\n"
|
|
" rom name=slot-3.rom size=0x80000\n"
|
|
" ram name=save.ram size=0x2000 volatile\n"
|
|
" map address=70-7d,f0-ff:0000-7fff mask=0x8000\n"
|
|
" necdsp model=uPD7725 frequency=8000000\n"
|
|
" map address=20-3f,a0-bf:8000-ffff mask=0x7fff\n"
|
|
" prom name=dsp1.program.rom size=0x1800\n"
|
|
" drom name=dsp1.data.rom size=0x800\n"
|
|
" dram name=dsp1.data.ram size=0x200 volatile\n"
|
|
);
|
|
return;
|
|
}
|
|
|
|
else if(type == Type::Powerfest94) {
|
|
markup.append(
|
|
" event=PF94 timer=360\n"
|
|
" map address=10,20:6000\n"
|
|
" map=mcu address=00-3f,80-bf:8000-ffff\n"
|
|
" map=mcu address=c0-ff:0000-ffff\n"
|
|
" rom name=program.rom size=0x40000\n"
|
|
" rom name=slot-1.rom size=0x80000\n"
|
|
" rom name=slot-2.rom size=0x80000\n"
|
|
" rom name=slot-3.rom size=0x100000\n"
|
|
" ram name=save.ram size=0x2000 volatile\n"
|
|
" map address=30-3f,b0-bf:6000-7fff mask=0xe000\n"
|
|
" necdsp model=uPD7725 frequency=8000000\n"
|
|
" map address=00-0f,80-8f:6000-7fff mask=0xfff\n"
|
|
" prom name=dsp1.program.rom size=0x1800\n"
|
|
" drom name=dsp1.data.rom size=0x800\n"
|
|
" dram name=dsp1.data.ram size=0x200 volatile\n"
|
|
);
|
|
return;
|
|
}
|
|
|
|
if(has_sharprtc) {
|
|
markup.append(
|
|
" sharprtc\n"
|
|
" map address=00-3f,80-bf:2800-2801\n"
|
|
" ram name=rtc.ram size=0x10\n"
|
|
);
|
|
}
|
|
|
|
if(has_epsonrtc) {
|
|
markup.append(
|
|
" epsonrtc\n"
|
|
" map address=00-3f,80-bf:4840-4842\n"
|
|
" ram name=rtc.ram size=0x10\n"
|
|
);
|
|
}
|
|
|
|
if(has_obc1) {
|
|
markup.append(
|
|
" obc1\n"
|
|
" map address=00-3f,80-bf:6000-7fff mask=0xe000\n"
|
|
" ram name=save.ram size=0x2000\n"
|
|
);
|
|
}
|
|
|
|
if(has_dsp1) {
|
|
markup.append(
|
|
" necdsp model=uPD7725 frequency=8000000\n"
|
|
);
|
|
if(dsp1_type == DSP1Type::LoROM1MB) markup.append(
|
|
" map address=20-3f,a0-bf:8000-ffff mask=0x3fff\n"
|
|
);
|
|
if(dsp1_type == DSP1Type::LoROM2MB) markup.append(
|
|
" map address=60-6f,e0-ef:0000-7fff mask=0x3fff\n"
|
|
);
|
|
if(dsp1_type == DSP1Type::HiROM) markup.append(
|
|
" map address=00-1f,80-9f:6000-7fff mask=0xfff\n"
|
|
);
|
|
markup.append(
|
|
" prom name=dsp1b.program.rom size=0x1800\n"
|
|
" drom name=dsp1b.data.rom size=0x800\n"
|
|
" dram name=dsp1b.data.ram size=0x200 volatile\n"
|
|
);
|
|
}
|
|
|
|
if(has_dsp2) {
|
|
markup.append(
|
|
" necdsp model=uPD7725 frequency=8000000\n"
|
|
" map address=20-3f,a0-bf:8000-ffff mask=0x3fff\n"
|
|
" prom name=dsp2.program.rom size=0x1800\n"
|
|
" drom name=dsp2.data.rom size=0x800\n"
|
|
" dram name=dsp2.data.ram size=0x200 volatile\n"
|
|
);
|
|
}
|
|
|
|
if(has_dsp3) {
|
|
markup.append(
|
|
" necdsp model=uPD7725 frequency=8000000\n"
|
|
" map address=20-3f,a0-bf:8000-ffff mask=0x3fff\n"
|
|
" prom name=dsp3.program.rom size=0x1800\n"
|
|
" drom name=dsp3.data.rom size=0x800\n"
|
|
" dram name=dsp3.data.ram size=0x200 volatile\n"
|
|
);
|
|
}
|
|
|
|
if(has_dsp4) {
|
|
markup.append(
|
|
" necdsp model=uPD7725 frequency=8000000\n"
|
|
" map address=30-3f,b0-bf:8000-ffff mask=0x3fff\n"
|
|
" prom name=dsp4.program.rom size=0x1800\n"
|
|
" drom name=dsp4.data.rom size=0x800\n"
|
|
" dram name=dsp4.data.ram size=0x200 volatile\n"
|
|
);
|
|
}
|
|
|
|
if(has_st010) {
|
|
markup.append(
|
|
" necdsp model=uPD96050 frequency=11000000\n"
|
|
" map address=60-67,e0-e7:0000-3fff\n"
|
|
" prom name=st010.program.rom size=0xc000\n"
|
|
" drom name=st010.data.rom size=0x1000\n"
|
|
" dram name=save.ram size=0x1000\n"
|
|
" map address=68-6f,e8-ef:0000-7fff mask=0x8000\n"
|
|
);
|
|
}
|
|
|
|
if(has_st011) {
|
|
markup.append(
|
|
" necdsp model=uPD96050 frequency=15000000\n"
|
|
" map address=60-67,e0-e7:0000-3fff\n"
|
|
" prom name=st011.program.rom size=0xc000\n"
|
|
" drom name=st011.data.rom size=0x1000\n"
|
|
" dram name=save.ram size=0x1000\n"
|
|
" map address=68-6f,e8-ef:0000-7fff mask=0x8000\n"
|
|
);
|
|
}
|
|
|
|
if(has_st018) {
|
|
markup.append(
|
|
" armdsp frequency=21477272\n"
|
|
" map address=00-3f,80-bf:3800-38ff\n"
|
|
" prom name=st018.program.rom size=0x20000\n"
|
|
" drom name=st018.data.rom size=0x8000\n"
|
|
" ram name=save.ram size=0x4000\n"
|
|
);
|
|
}
|
|
|
|
if(has_msu1) {
|
|
markup.append(
|
|
" msu1\n"
|
|
" map address=00-3f,80-bf:2000-2007\n"
|
|
" rom name=msu1.rom\n"
|
|
);
|
|
}
|
|
}
|
|
|
|
auto SuperFamicomCartridge::readHeader(const uint8_t* data, uint size) -> void {
|
|
//detect Game Boy carts
|
|
if(size >= 0x0140) {
|
|
if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66
|
|
&& data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) {
|
|
type = Type::GameBoy;
|
|
return;
|
|
}
|
|
}
|
|
|
|
const uint index = findHeader(data, size);
|
|
const uint8_t mapperid = data[index + Mapper];
|
|
const uint8_t rom_type = data[index + RomType];
|
|
const uint8_t rom_size = data[index + RomSize];
|
|
const uint8_t company = data[index + Company];
|
|
const uint8_t regionid = data[index + CartRegion] & 0x7f;
|
|
|
|
const uint16_t complement = data[index + Complement] | data[index + Complement + 1] << 8;
|
|
const uint16_t checksum = data[index + Checksum] | data[index + Checksum + 1] << 8;
|
|
|
|
this->rom_size = size;
|
|
ram_size = 1024 << (data[index + RamSize] & 7);
|
|
if(ram_size == 1024) ram_size = 0; //no RAM present
|
|
if(rom_size == 0 && ram_size) ram_size = 0; //fix for Bazooka Blitzkrieg's malformed header (swapped ROM and RAM sizes)
|
|
|
|
//0, 1, 13 = NTSC; 2 - 12 = PAL
|
|
region = (regionid <= 1 || regionid >= 13) ? Region::NTSC : Region::PAL;
|
|
|
|
//detect BS-X flash carts
|
|
if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) {
|
|
if(data[index + 0x14] == 0x00) {
|
|
const uint8_t n15 = data[index + 0x15];
|
|
if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) {
|
|
if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) {
|
|
type = Type::Satellaview;
|
|
region = Region::NTSC; //BS-X only released in Japan
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//detect Sufami Turbo carts
|
|
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
|
|
if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) {
|
|
type = Type::SufamiTurboBIOS;
|
|
} else {
|
|
type = Type::SufamiTurbo;
|
|
}
|
|
region = Region::NTSC; //Sufami Turbo only released in Japan
|
|
return; //RAM size handled outside this routine
|
|
}
|
|
|
|
//detect Super Game Boy BIOS
|
|
if(!memcmp(data + index, "Super GAMEBOY2", 14)) {
|
|
type = Type::SuperGameBoy2BIOS;
|
|
return;
|
|
}
|
|
|
|
if(!memcmp(data + index, "Super GAMEBOY", 13)) {
|
|
type = Type::SuperGameBoy1BIOS;
|
|
return;
|
|
}
|
|
|
|
//detect competition carts
|
|
if(!memcmp(data + index, "\x00\x08\x22\x02\x1c\x00\x10\x00\x08\x65\x80\x84\x20\x00\x22\x25\x00\x83\x0c\x80\x10", 21)
|
|
&& complement == 0x0100 && checksum == 0x2d02 && (size == 0x1c0000 || size == 0x1c2000)) {
|
|
type = Type::CampusChallenge92; //dark title screen version
|
|
return;
|
|
}
|
|
|
|
if(!memcmp(data + index, "\xc9\x80\x80\x44\x15\x00\x62\x09\x29\xa0\x52\x70\x50\x12\x05\x35\x31\x63\xc0\x22\x01", 21)
|
|
&& complement == 0x2011 && checksum == 0xf8c0 && (size == 0x240000 || size == 0x242000)) {
|
|
type = Type::Powerfest94; //10,000 points version
|
|
return;
|
|
}
|
|
|
|
if(!memcmp(data + index, "PREHISTORIK MAN ", 21)
|
|
&& complement == 0xffff && checksum == 0x0000 && (size == 0x240000 || size == 0x242000)) {
|
|
type = Type::Powerfest94; //1,000,000 points version
|
|
return;
|
|
}
|
|
|
|
//detect presence of BS-X flash cartridge connector (reads extended header information)
|
|
if(data[index - 14] == 'Z') {
|
|
if(data[index - 11] == 'J') {
|
|
uint8_t n13 = data[index - 13];
|
|
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
|
|
if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) {
|
|
has_bsx_slot = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(has_bsx_slot) {
|
|
if(!memcmp(data + index, "Satellaview BS-X ", 21)) {
|
|
//BS-X base cart
|
|
type = Type::SatellaviewBIOS;
|
|
region = Region::NTSC; //BS-X only released in Japan
|
|
return; //RAM size handled internally by load_cart_bsx() -> BSXCart class
|
|
} else {
|
|
type = (index == 0x7fc0 ? Type::LoROMSatellaview : Type::HiROMSatellaview);
|
|
region = Region::NTSC; //BS-X slotted cartridges only released in Japan
|
|
}
|
|
} else {
|
|
//standard cart
|
|
if(index == 0x7fc0 && size >= 0x401000) {
|
|
type = Type::ExLoROM;
|
|
} else if(index == 0x7fc0 && mapperid == 0x32) {
|
|
type = Type::ExLoROM;
|
|
} else if(index == 0x7fc0) {
|
|
type = Type::LoROM;
|
|
} else if(index == 0xffc0) {
|
|
type = Type::HiROM;
|
|
} else if(index == 0x40ffc0) {
|
|
type = Type::ExHiROM;
|
|
} else {
|
|
type = Type::Unknown;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) {
|
|
has_superfx = true;
|
|
type = Type::SuperFX;
|
|
ram_size = 1024 << (data[index - 3] & 7);
|
|
if(ram_size == 1024) ram_size = 0;
|
|
}
|
|
|
|
if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) {
|
|
has_sa1 = true;
|
|
type = Type::SA1;
|
|
}
|
|
|
|
if(mapperid == 0x35 && rom_type == 0x55) {
|
|
has_sharprtc = true;
|
|
}
|
|
|
|
if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) {
|
|
has_sdd1 = true;
|
|
}
|
|
|
|
if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) {
|
|
has_spc7110 = true;
|
|
has_epsonrtc = (rom_type == 0xf9);
|
|
}
|
|
|
|
if(mapperid == 0x20 && rom_type == 0xf3) {
|
|
has_cx4 = true;
|
|
}
|
|
|
|
if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) {
|
|
has_dsp1 = true;
|
|
}
|
|
|
|
if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) {
|
|
has_dsp1 = true;
|
|
}
|
|
|
|
if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) {
|
|
has_dsp1 = true;
|
|
}
|
|
|
|
if(has_dsp1 == true) {
|
|
if((mapperid & 0x2f) == 0x20 && size <= 0x100000) {
|
|
dsp1_type = DSP1Type::LoROM1MB;
|
|
} else if((mapperid & 0x2f) == 0x20) {
|
|
dsp1_type = DSP1Type::LoROM2MB;
|
|
} else if((mapperid & 0x2f) == 0x21) {
|
|
dsp1_type = DSP1Type::HiROM;
|
|
}
|
|
}
|
|
|
|
if(mapperid == 0x20 && rom_type == 0x05) {
|
|
has_dsp2 = true;
|
|
}
|
|
|
|
if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) {
|
|
has_dsp3 = true;
|
|
}
|
|
|
|
if(mapperid == 0x30 && rom_type == 0x03) {
|
|
has_dsp4 = true;
|
|
}
|
|
|
|
if(mapperid == 0x30 && rom_type == 0x25) {
|
|
has_obc1 = true;
|
|
}
|
|
|
|
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) {
|
|
has_st010 = true;
|
|
}
|
|
|
|
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) {
|
|
has_st011 = true;
|
|
}
|
|
|
|
if(mapperid == 0x30 && rom_type == 0xf5) {
|
|
has_st018 = true;
|
|
}
|
|
}
|
|
|
|
auto SuperFamicomCartridge::findHeader(const uint8_t* data, uint size) -> uint {
|
|
uint score_lo = scoreHeader(data, size, 0x007fc0);
|
|
uint score_hi = scoreHeader(data, size, 0x00ffc0);
|
|
uint score_ex = scoreHeader(data, size, 0x40ffc0);
|
|
if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits
|
|
|
|
if(score_lo >= score_hi && score_lo >= score_ex) {
|
|
return 0x007fc0;
|
|
} else if(score_hi >= score_ex) {
|
|
return 0x00ffc0;
|
|
} else {
|
|
return 0x40ffc0;
|
|
}
|
|
}
|
|
|
|
auto SuperFamicomCartridge::scoreHeader(const uint8_t* data, uint size, uint addr) -> uint {
|
|
if(size < addr + 64) return 0; //image too small to contain header at this location?
|
|
int score = 0;
|
|
|
|
uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8);
|
|
uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8);
|
|
uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8);
|
|
|
|
uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset
|
|
uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit
|
|
|
|
//$00:[000-7fff] contains uninitialized RAM and MMIO.
|
|
//reset vector must point to ROM at $00:[8000-ffff] to be considered valid.
|
|
if(resetvector < 0x8000) return 0;
|
|
|
|
//some images duplicate the header in multiple locations, and others have completely
|
|
//invalid header information that cannot be relied upon.
|
|
//below code will analyze the first opcode executed at the specified reset vector to
|
|
//determine the probability that this is the correct header.
|
|
|
|
//most likely opcodes
|
|
if(resetop == 0x78 //sei
|
|
|| resetop == 0x18 //clc (clc; xce)
|
|
|| resetop == 0x38 //sec (sec; xce)
|
|
|| resetop == 0x9c //stz $nnnn (stz $4200)
|
|
|| resetop == 0x4c //jmp $nnnn
|
|
|| resetop == 0x5c //jml $nnnnnn
|
|
) score += 8;
|
|
|
|
//plausible opcodes
|
|
if(resetop == 0xc2 //rep #$nn
|
|
|| resetop == 0xe2 //sep #$nn
|
|
|| resetop == 0xad //lda $nnnn
|
|
|| resetop == 0xae //ldx $nnnn
|
|
|| resetop == 0xac //ldy $nnnn
|
|
|| resetop == 0xaf //lda $nnnnnn
|
|
|| resetop == 0xa9 //lda #$nn
|
|
|| resetop == 0xa2 //ldx #$nn
|
|
|| resetop == 0xa0 //ldy #$nn
|
|
|| resetop == 0x20 //jsr $nnnn
|
|
|| resetop == 0x22 //jsl $nnnnnn
|
|
) score += 4;
|
|
|
|
//implausible opcodes
|
|
if(resetop == 0x40 //rti
|
|
|| resetop == 0x60 //rts
|
|
|| resetop == 0x6b //rtl
|
|
|| resetop == 0xcd //cmp $nnnn
|
|
|| resetop == 0xec //cpx $nnnn
|
|
|| resetop == 0xcc //cpy $nnnn
|
|
) score -= 4;
|
|
|
|
//least likely opcodes
|
|
if(resetop == 0x00 //brk #$nn
|
|
|| resetop == 0x02 //cop #$nn
|
|
|| resetop == 0xdb //stp
|
|
|| resetop == 0x42 //wdm
|
|
|| resetop == 0xff //sbc $nnnnnn,x
|
|
) score -= 8;
|
|
|
|
//at times, both the header and reset vector's first opcode will match ...
|
|
//fallback and rely on info validity in these cases to determine more likely header.
|
|
|
|
//a valid checksum is the biggest indicator of a valid header.
|
|
if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4;
|
|
|
|
if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM
|
|
if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM
|
|
if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM
|
|
if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM
|
|
|
|
if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header
|
|
if(data[addr + RomType] < 0x08) score++;
|
|
if(data[addr + RomSize] < 0x10) score++;
|
|
if(data[addr + RamSize] < 0x08) score++;
|
|
if(data[addr + CartRegion] < 14) score++;
|
|
|
|
if(score < 0) score = 0;
|
|
return score;
|
|
}
|