diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index f6ee7d0c..ff46d189 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "106.02"; + static const string Version = "106.03"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org/"; diff --git a/higan/sfc/cartridge/load.cpp b/higan/sfc/cartridge/load.cpp index 0de72543..a99a92ac 100644 --- a/higan/sfc/cartridge/load.cpp +++ b/higan/sfc/cartridge/load.cpp @@ -12,7 +12,7 @@ auto Cartridge::loadBoard(Markup::Node node) -> Markup::Node { if(auto fp = platform->open(ID::System, "boards.bml", File::Read, File::Required)) { auto document = BML::unserialize(fp->reads()); for(auto leaf : document.find("board")) { - auto id = leaf["id"].text(); + auto id = leaf.text(); bool matched = id == board; if(!matched && id.match("*(*)*")) { auto part = id.transform("()", "||").split("|"); @@ -31,7 +31,8 @@ auto Cartridge::loadBoard(Markup::Node node) -> Markup::Node { output.append("region=pal\n"); } uint counter = 0; - for(auto& line : leaf.text().split("\n")) { + for(auto& line : BML::serialize(leaf).split("\n")) { + line.trimLeft(" ", 1L); if(line.endsWith("rom") || line.endsWith("ram")) { auto memory = node.find("game/memory"); if(counter < memory.size()) { diff --git a/higan/sfc/coprocessor/event/event.cpp b/higan/sfc/coprocessor/event/event.cpp index 5cef991a..5bcb7cb4 100644 --- a/higan/sfc/coprocessor/event/event.cpp +++ b/higan/sfc/coprocessor/event/event.cpp @@ -39,6 +39,12 @@ auto Event::unload() -> void { auto Event::power() -> void { create(Event::Enter, 1); + rom[0].writeProtect(true); + rom[1].writeProtect(true); + rom[2].writeProtect(true); + rom[3].writeProtect(true); + ram.writeProtect(false); + for(auto n : range(ram.size())) ram.write(n, 0x00); status = 0x00; select = 0x00; diff --git a/higan/sfc/coprocessor/hitachidsp/hitachidsp.cpp b/higan/sfc/coprocessor/hitachidsp/hitachidsp.cpp index 9a5e35da..ed164e1c 100644 --- a/higan/sfc/coprocessor/hitachidsp/hitachidsp.cpp +++ b/higan/sfc/coprocessor/hitachidsp/hitachidsp.cpp @@ -34,6 +34,9 @@ auto HitachiDSP::power() -> void { HG51B::power(); create(HitachiDSP::Enter, Frequency); + rom.writeProtect(true); + ram.writeProtect(false); + mmio.dma = false; mmio.dmaSource = 0x000000; diff --git a/higan/sfc/coprocessor/mcc/mcc.cpp b/higan/sfc/coprocessor/mcc/mcc.cpp index 6d2f1447..26ed783a 100644 --- a/higan/sfc/coprocessor/mcc/mcc.cpp +++ b/higan/sfc/coprocessor/mcc/mcc.cpp @@ -11,6 +11,9 @@ auto MCC::unload() -> void { } auto MCC::power() -> void { + rom.writeProtect(true); + ram.writeProtect(false); + for(auto n : range(16)) r[n] = 0x00; r[0x07] = 0x80; r[0x08] = 0x80; diff --git a/higan/sfc/coprocessor/obc1/obc1.cpp b/higan/sfc/coprocessor/obc1/obc1.cpp index 691822e9..355a3411 100644 --- a/higan/sfc/coprocessor/obc1/obc1.cpp +++ b/higan/sfc/coprocessor/obc1/obc1.cpp @@ -10,6 +10,8 @@ auto OBC1::unload() -> void { } auto OBC1::power() -> void { + ram.writeProtect(false); + status.baseptr = (ramRead(0x1ff5) & 1) ? 0x1800 : 0x1c00; status.address = (ramRead(0x1ff6) & 0x7f); status.shift = (ramRead(0x1ff6) & 3) << 1; diff --git a/higan/sfc/coprocessor/sa1/sa1.cpp b/higan/sfc/coprocessor/sa1/sa1.cpp index 2b305ff9..1b9260ec 100644 --- a/higan/sfc/coprocessor/sa1/sa1.cpp +++ b/higan/sfc/coprocessor/sa1/sa1.cpp @@ -127,6 +127,10 @@ auto SA1::power() -> void { WDC65816::power(); create(SA1::Enter, system.cpuFrequency()); + rom.writeProtect(true); + bwram.writeProtect(false); + iram.writeProtect(false); + cpubwram.dma = false; for(auto addr : range(iram.size())) { iram.write(addr, 0x00); diff --git a/higan/sfc/coprocessor/sdd1/sdd1.cpp b/higan/sfc/coprocessor/sdd1/sdd1.cpp index 69f8676c..ea05ab23 100644 --- a/higan/sfc/coprocessor/sdd1/sdd1.cpp +++ b/higan/sfc/coprocessor/sdd1/sdd1.cpp @@ -13,6 +13,9 @@ auto SDD1::unload() -> void { } auto SDD1::power() -> void { + rom.writeProtect(true); + ram.writeProtect(false); + //hook S-CPU DMA MMIO registers to gather information for struct dma[]; //buffer address and transfer size information for use in SDD1::mcu_read() bus.map({&SDD1::dmaRead, &sdd1}, {&SDD1::dmaWrite, &sdd1}, "00-3f,80-bf:4300-437f"); diff --git a/higan/sfc/coprocessor/spc7110/spc7110.cpp b/higan/sfc/coprocessor/spc7110/spc7110.cpp index 14425d8f..705555a3 100644 --- a/higan/sfc/coprocessor/spc7110/spc7110.cpp +++ b/higan/sfc/coprocessor/spc7110/spc7110.cpp @@ -41,6 +41,10 @@ auto SPC7110::unload() -> void { auto SPC7110::power() -> void { create(SPC7110::Enter, 21'477'272); + prom.writeProtect(true); + drom.writeProtect(true); + ram.writeProtect(true); + r4801 = 0x00; r4802 = 0x00; r4803 = 0x00; diff --git a/higan/sfc/coprocessor/superfx/superfx.cpp b/higan/sfc/coprocessor/superfx/superfx.cpp index 8f132184..5e4d7459 100644 --- a/higan/sfc/coprocessor/superfx/superfx.cpp +++ b/higan/sfc/coprocessor/superfx/superfx.cpp @@ -40,6 +40,9 @@ auto SuperFX::power() -> void { GSU::power(); create(SuperFX::Enter, system.cpuFrequency()); + rom.writeProtect(true); + ram.writeProtect(false); + romMask = rom.size() - 1; ramMask = ram.size() - 1; diff --git a/higan/sfc/slot/sufamiturbo/sufamiturbo.cpp b/higan/sfc/slot/sufamiturbo/sufamiturbo.cpp index 15c1131f..e1469b32 100644 --- a/higan/sfc/slot/sufamiturbo/sufamiturbo.cpp +++ b/higan/sfc/slot/sufamiturbo/sufamiturbo.cpp @@ -11,4 +11,9 @@ auto SufamiTurboCartridge::unload() -> void { ram.reset(); } +auto SufamiTurboCartridge::power() -> void { + rom.writeProtect(true); + ram.writeProtect(false); +} + } diff --git a/higan/sfc/slot/sufamiturbo/sufamiturbo.hpp b/higan/sfc/slot/sufamiturbo/sufamiturbo.hpp index 75ad8782..ea73942f 100644 --- a/higan/sfc/slot/sufamiturbo/sufamiturbo.hpp +++ b/higan/sfc/slot/sufamiturbo/sufamiturbo.hpp @@ -1,5 +1,6 @@ struct SufamiTurboCartridge { auto unload() -> void; + auto power() -> void; auto serialize(serializer&) -> void; uint pathID = 0; diff --git a/higan/sfc/system/system.cpp b/higan/sfc/system/system.cpp index 58ce46f7..29d9068d 100644 --- a/higan/sfc/system/system.cpp +++ b/higan/sfc/system/system.cpp @@ -120,6 +120,7 @@ auto System::power(bool reset) -> void { if(cartridge.has.OBC1) obc1.power(); if(cartridge.has.MSU1) msu1.power(); if(cartridge.has.BSMemorySlot) bsmemory.power(); + if(cartridge.has.SufamiTurboSlots) sufamiturboA.power(), sufamiturboB.power(); if(cartridge.has.ICD2) cpu.coprocessors.append(&icd2); if(cartridge.has.Event) cpu.coprocessors.append(&event); diff --git a/higan/systems/Super Famicom.sys/boards.bml b/higan/systems/Super Famicom.sys/boards.bml index bafb9ecd..a7cb1b35 100644 --- a/higan/systems/Super Famicom.sys/boards.bml +++ b/higan/systems/Super Famicom.sys/boards.bml @@ -1,407 +1,408 @@ -database type=sfc-boards revision=2017-12-29 +database + revision: 2017-12-29 -board id:1A0N-(01,02,10,20,30) - :rom - : map address=00-7d,80-ff:8000-ffff mask=0x8000 - : map address=40-7d,c0-ff:0000-7fff mask=0x8000 +board: 1A0N-(01,02,10,20,30) + rom + map address=00-7d,80-ff:8000-ffff mask=0x8000 + map address=40-7d,c0-ff:0000-7fff mask=0x8000 -board id:1A1B-(04,05,06) - :rom - : map address=00-1f,80-9f:8000-ffff mask=0x8000 - :ram - : map address=70-7d,f0-ff:0000-ffff +board: 1A1B-(04,05,06) + rom + map address=00-1f,80-9f:8000-ffff mask=0x8000 + ram + map address=70-7d,f0-ff:0000-ffff -board id:1A1M-(01,10,11,20) - :rom - : map address=00-7d,80-ff:8000-ffff mask=0x8000 - :ram - : map address=70-7d,f0-ff:0000-7fff mask=0x8000 +board: 1A1M-(01,10,11,20) + rom + map address=00-7d,80-ff:8000-ffff mask=0x8000 + ram + map address=70-7d,f0-ff:0000-7fff mask=0x8000 -board id:1A3B-(11,12,13) - :rom - : map address=00-1f,80-9f:8000-ffff mask=0x8000 - :ram - : map address=70-7d,f0-ff:0000-ffff +board: 1A3B-(11,12,13) + rom + map address=00-1f,80-9f:8000-ffff mask=0x8000 + ram + map address=70-7d,f0-ff:0000-ffff -board id:1A3B-20 - :rom - : map address=00-7d,80-ff:8000-ffff mask=0x8000 - :ram - : map address=70-7d,f0-ff:0000-7fff mask=0x8000 +board: 1A3B-20 + rom + map address=00-7d,80-ff:8000-ffff mask=0x8000 + ram + map address=70-7d,f0-ff:0000-7fff mask=0x8000 -board id:1A3M-(10,20,21,30) - :rom - : map address=00-7d,80-ff:8000-ffff mask=0x8000 - :ram - : map address=70-7d,f0-ff:0000-7fff mask=0x8000 +board: 1A3M-(10,20,21,30) + rom + map address=00-7d,80-ff:8000-ffff mask=0x8000 + ram + map address=70-7d,f0-ff:0000-7fff mask=0x8000 -board id:1A5B-(02,04) - :rom - : map address=00-1f,80-9f:8000-ffff mask=0x8000 - :ram - : map address=70-7d,f0-ff:0000-ffff +board: 1A5B-(02,04) + rom + map address=00-1f,80-9f:8000-ffff mask=0x8000 + ram + map address=70-7d,f0-ff:0000-ffff -board id:1A5M-(01,11,20) - :rom - : map address=00-7d,80-ff:8000-ffff mask=0x8000 - :ram - : map address=70-7d,f0-ff:0000-7fff mask=0x8000 +board: 1A5M-(01,11,20) + rom + map address=00-7d,80-ff:8000-ffff mask=0x8000 + ram + map address=70-7d,f0-ff:0000-7fff mask=0x8000 -board id:1B0N-(02,03,10) - :rom - : map address=00-1f,80-9f:8000-ffff mask=0x8000 - :necdsp model=uPD7725 frequency=8000000 - : map address=30-3f,b0-bf:8000-ffff mask=0x3fff - : prom - : drom - : dram +board: 1B0N-(02,03,10) + rom + map address=00-1f,80-9f:8000-ffff mask=0x8000 + necdsp model=uPD7725 frequency=8000000 + map address=30-3f,b0-bf:8000-ffff mask=0x3fff + prom + drom + dram -board id:1B5B-02 - :rom - : map address=00-1f,80-9f:8000-ffff mask=0x8000 - :ram - : map address=70-7d,f0-ff:0000-ffff - :necdsp model=uPD7725 frequency=8000000 - : map address=20-3f,a0-bf:8000-ffff mask=0x3fff - : prom - : drom - : dram +board: 1B5B-02 + rom + map address=00-1f,80-9f:8000-ffff mask=0x8000 + ram + map address=70-7d,f0-ff:0000-ffff + necdsp model=uPD7725 frequency=8000000 + map address=20-3f,a0-bf:8000-ffff mask=0x3fff + prom + drom + dram -board id:1C0N - :superfx - : map address=00-3f,80-bf:3000-34ff - : rom - : map address=00-1f,80-9f:8000-ffff mask=0x8000 - : ram - : map address=60-7d,e0-ff:0000-ffff +board: 1C0N + superfx + map address=00-3f,80-bf:3000-34ff + rom + map address=00-1f,80-9f:8000-ffff mask=0x8000 + ram + map address=60-7d,e0-ff:0000-ffff -board id:1C0N5S-01 - :superfx - : map address=00-3f,80-bf:3000-34ff - : rom - : map address=00-1f,80-9f:8000-ffff mask=0x8000 - : ram - : map address=60-7d,e0-ff:0000-ffff +board: 1C0N5S-01 + superfx + map address=00-3f,80-bf:3000-34ff + rom + map address=00-1f,80-9f:8000-ffff mask=0x8000 + ram + map address=60-7d,e0-ff:0000-ffff -board id:1CA0N5S-01 - :superfx - : map address=00-3f,80-bf:3000-34ff - : rom - : map address=00-3f,80-bf:8000-ffff mask=0x8000 - : map address=40-5f,c0-df:0000-ffff - : ram - : map address=00-3f,80-bf:6000-7fff size=0x2000 - : map address=70-71,f0-f1:0000-ffff +board: 1CA0N5S-01 + superfx + map address=00-3f,80-bf:3000-34ff + rom + map address=00-3f,80-bf:8000-ffff mask=0x8000 + map address=40-5f,c0-df:0000-ffff + ram + map address=00-3f,80-bf:6000-7fff size=0x2000 + map address=70-71,f0-f1:0000-ffff -board id:1CA0N6S-01 - :superfx - : map address=00-3f,80-bf:3000-34ff - : rom - : map address=00-3f,80-bf:8000-ffff mask=0x8000 - : map address=40-5f,c0-df:0000-ffff - : ram - : map address=00-3f,80-bf:6000-7fff size=0x2000 - : map address=70-71,f0-f1:0000-ffff +board: 1CA0N6S-01 + superfx + map address=00-3f,80-bf:3000-34ff + rom + map address=00-3f,80-bf:8000-ffff mask=0x8000 + map address=40-5f,c0-df:0000-ffff + ram + map address=00-3f,80-bf:6000-7fff size=0x2000 + map address=70-71,f0-f1:0000-ffff -board id:1CA6B-01 - :superfx - : map address=00-3f,80-bf:3000-34ff - : rom - : map address=00-3f,80-bf:8000-ffff mask=0x8000 - : map address=40-5f,c0-df:0000-ffff - : ram - : map address=00-3f,80-bf:6000-7fff size=0x2000 - : map address=70-71,f0-f1:0000-ffff +board: 1CA6B-01 + superfx + map address=00-3f,80-bf:3000-34ff + rom + map address=00-3f,80-bf:8000-ffff mask=0x8000 + map address=40-5f,c0-df:0000-ffff + ram + map address=00-3f,80-bf:6000-7fff size=0x2000 + map address=70-71,f0-f1:0000-ffff -board id:1CB0N7S-01 - :superfx - : map address=00-3f,80-bf:3000-34ff - : rom - : map address=00-3f:8000-ffff mask=0x8000 - : map address=40-5f:0000-ffff - : ram - : map address=00-3f,80-bf:6000-7fff size=0x2000 - : map address=70-71:0000-ffff +board: 1CB0N7S-01 + superfx + map address=00-3f,80-bf:3000-34ff + rom + map address=00-3f:8000-ffff mask=0x8000 + map address=40-5f:0000-ffff + ram + map address=00-3f,80-bf:6000-7fff size=0x2000 + map address=70-71:0000-ffff -board id:1CB5B-20 - :superfx - : map address=00-3f,80-bf:3000-34ff - : rom - : map address=00-3f:8000-ffff mask=0x8000 - : map address=40-5f:0000-ffff - : ram - : map address=00-3f,80-bf:6000-7fff size=0x2000 - : map address=70-71:0000-ffff +board: 1CB5B-20 + superfx + map address=00-3f,80-bf:3000-34ff + rom + map address=00-3f:8000-ffff mask=0x8000 + map address=40-5f:0000-ffff + ram + map address=00-3f,80-bf:6000-7fff size=0x2000 + map address=70-71:0000-ffff -board id:1CB7B-01 - :superfx - : map address=00-3f,80-bf:3000-34ff - : rom - : map address=00-3f:8000-ffff mask=0x8000 - : map address=40-5f:0000-ffff - : ram - : map address=00-3f,80-bf:6000-7fff size=0x2000 - : map address=70-71:0000-ffff +board: 1CB7B-01 + superfx + map address=00-3f,80-bf:3000-34ff + rom + map address=00-3f:8000-ffff mask=0x8000 + map address=40-5f:0000-ffff + ram + map address=00-3f,80-bf:6000-7fff size=0x2000 + map address=70-71:0000-ffff -board id:1DC0N-01 - :hitachidsp model=HG51B169 frequency=20000000 - : map address=00-3f,80-bf:6c00-6fff,7c00-7fff - : rom - : map address=00-3f,80-bf:8000-ffff mask=0x8000 - : ram - : map address=70-77:0000-7fff mask=0x8000 - : drom - : dram - : map address=00-3f,80-bf:6000-6bff,7000-7bff mask=0xf000 +board: 1DC0N-01 + hitachidsp model=HG51B169 frequency=20000000 + map address=00-3f,80-bf:6c00-6fff,7c00-7fff + rom + map address=00-3f,80-bf:8000-ffff mask=0x8000 + ram + map address=70-77:0000-7fff mask=0x8000 + drom + dram + map address=00-3f,80-bf:6000-6bff,7000-7bff mask=0xf000 -board id:1DS0B-20 - :rom - : map address=00-7d,80-ff:8000-ffff mask=0x8000 - :necdsp model=uPD96050 frequency=11000000 - : map address=60-67,e0-e7:0000-3fff - : prom - : drom - : dram - : map address=68-6f,e8-ef:0000-7fff mask=0x8000 +board: 1DS0B-20 + rom + map address=00-7d,80-ff:8000-ffff mask=0x8000 + necdsp model=uPD96050 frequency=11000000 + map address=60-67,e0-e7:0000-3fff + prom + drom + dram + map address=68-6f,e8-ef:0000-7fff mask=0x8000 -board id:1J0N-(01,10,20) - :rom - : map address=00-3f,80-bf:8000-ffff - : map address=40-7d,c0-ff:0000-ffff +board: 1J0N-(01,10,20) + rom + map address=00-3f,80-bf:8000-ffff + map address=40-7d,c0-ff:0000-ffff -board id:1J1M-(11,20) - :rom - : map address=00-3f,80-bf:8000-ffff - : map address=40-7d,c0-ff:0000-ffff - :ram - : map address=20-3f,a0-bf:6000-7fff mask=0xe000 +board: 1J1M-(11,20) + rom + map address=00-3f,80-bf:8000-ffff + map address=40-7d,c0-ff:0000-ffff + ram + map address=20-3f,a0-bf:6000-7fff mask=0xe000 -board id:1J3B-01 - :rom - : map address=00-3f,80-bf:8000-ffff - : map address=40-7d,c0-ff:0000-ffff - :ram - : map address=20-3f,a0-bf:6000-7fff mask=0xe000 +board: 1J3B-01 + rom + map address=00-3f,80-bf:8000-ffff + map address=40-7d,c0-ff:0000-ffff + ram + map address=20-3f,a0-bf:6000-7fff mask=0xe000 -board id:1J3M-(01,11,20) - :rom - : map address=00-3f,80-bf:8000-ffff - : map address=40-7d,c0-ff:0000-ffff - :ram - : map address=20-3f,a0-bf:6000-7fff mask=0xe000 +board: 1J3M-(01,11,20) + rom + map address=00-3f,80-bf:8000-ffff + map address=40-7d,c0-ff:0000-ffff + ram + map address=20-3f,a0-bf:6000-7fff mask=0xe000 -board id:1J5M-(11,20) - :rom - : map address=00-3f,80-bf:8000-ffff - : map address=40-7d,c0-ff:0000-ffff - :ram - : map address=20-3f,a0-bf:6000-7fff mask=0xe000 +board: 1J5M-(11,20) + rom + map address=00-3f,80-bf:8000-ffff + map address=40-7d,c0-ff:0000-ffff + ram + map address=20-3f,a0-bf:6000-7fff mask=0xe000 -board id:1K0N-01 - :rom - : map address=00-3f,80-bf:8000-ffff - : map address=40-7d,c0-ff:0000-ffff - :necdsp model=uPD7725 frequency=8000000 - : map address=00-1f,80-9f:6000-7fff mask=0xfff - : prom - : drom - : dram +board: 1K0N-01 + rom + map address=00-3f,80-bf:8000-ffff + map address=40-7d,c0-ff:0000-ffff + necdsp model=uPD7725 frequency=8000000 + map address=00-1f,80-9f:6000-7fff mask=0xfff + prom + drom + dram -board id:1K1B-01 - :rom - : map address=00-3f,80-bf:8000-ffff - : map address=40-7d,c0-ff:0000-ffff - :ram - : map address=20-3f,a0-bf:6000-7fff mask=0xe000 - :necdsp model=uPD7725 frequency=8000000 - : map address=00-1f,80-9f:6000-7fff mask=0xfff - : prom - : drom - : dram +board: 1K1B-01 + rom + map address=00-3f,80-bf:8000-ffff + map address=40-7d,c0-ff:0000-ffff + ram + map address=20-3f,a0-bf:6000-7fff mask=0xe000 + necdsp model=uPD7725 frequency=8000000 + map address=00-1f,80-9f:6000-7fff mask=0xfff + prom + drom + dram -board id:1L0N3S-01 - :sa1 - : map address=00-3f,80-bf:2200-23ff - : rom - : map address=00-3f,80-bf:8000-ffff mask=0x408000 - : map address=c0-ff:0000-ffff - : bwram - : map address=00-3f,80-bf:6000-7fff size=0x2000 - : map address=40-4f:0000-ffff - : iram - : map address=00-3f,80-bf:3000-37ff size=0x800 +board: 1L0N3S-01 + sa1 + map address=00-3f,80-bf:2200-23ff + rom + map address=00-3f,80-bf:8000-ffff mask=0x408000 + map address=c0-ff:0000-ffff + bwram + map address=00-3f,80-bf:6000-7fff size=0x2000 + map address=40-4f:0000-ffff + iram + map address=00-3f,80-bf:3000-37ff size=0x800 -board id:1L3B-(02,11) - :sa1 - : map address=00-3f,80-bf:2200-23ff - : rom - : map address=00-3f,80-bf:8000-ffff mask=0x408000 - : map address=c0-ff:0000-ffff - : bwram - : map address=00-3f,80-bf:6000-7fff size=0x2000 - : map address=40-4f:0000-ffff - : iram - : map address=00-3f,80-bf:3000-37ff size=0x800 +board: 1L3B-(02,11) + sa1 + map address=00-3f,80-bf:2200-23ff + rom + map address=00-3f,80-bf:8000-ffff mask=0x408000 + map address=c0-ff:0000-ffff + bwram + map address=00-3f,80-bf:6000-7fff size=0x2000 + map address=40-4f:0000-ffff + iram + map address=00-3f,80-bf:3000-37ff size=0x800 -board id:1L5B-(11,20) - :sa1 - : map address=00-3f,80-bf:2200-23ff - : rom - : map address=00-3f,80-bf:8000-ffff mask=0x408000 - : map address=c0-ff:0000-ffff - : bwram - : map address=00-3f,80-bf:6000-7fff size=0x2000 - : map address=40-4f:0000-ffff - : iram - : map address=00-3f,80-bf:3000-37ff size=0x800 +board: 1L5B-(11,20) + sa1 + map address=00-3f,80-bf:2200-23ff + rom + map address=00-3f,80-bf:8000-ffff mask=0x408000 + map address=c0-ff:0000-ffff + bwram + map address=00-3f,80-bf:6000-7fff size=0x2000 + map address=40-4f:0000-ffff + iram + map address=00-3f,80-bf:3000-37ff size=0x800 -board id:1N0N-01 - :sdd1 - : map address=00-3f,80-bf:4800-480f - : rom - : map address=00-3f,80-bf:8000-ffff - : map address=c0-ff:0000-ffff +board: 1N0N-01 + sdd1 + map address=00-3f,80-bf:4800-480f + rom + map address=00-3f,80-bf:8000-ffff + map address=c0-ff:0000-ffff -board id:2A0N-(01,10,11,20) - :rom - : map address=00-7d,80-ff:8000-ffff mask=0x8000 - : map address=40-7d,c0-ff:0000-7fff mask=0x8000 +board: 2A0N-(01,10,11,20) + rom + map address=00-7d,80-ff:8000-ffff mask=0x8000 + map address=40-7d,c0-ff:0000-7fff mask=0x8000 -board id:2A1M-01 - :rom - : map address=00-7d,80-ff:8000-ffff mask=0x8000 - :ram - : map address=70-7d,f0-ff:0000-7fff mask=0x8000 +board: 2A1M-01 + rom + map address=00-7d,80-ff:8000-ffff mask=0x8000 + ram + map address=70-7d,f0-ff:0000-7fff mask=0x8000 -board id:2A3B-01 - :rom - : map address=00-3f,80-bf:8000-ffff mask=0x8000 - :ram - : map address=70-7d,f0-ff:0000-7fff mask=0x8000 +board: 2A3B-01 + rom + map address=00-3f,80-bf:8000-ffff mask=0x8000 + ram + map address=70-7d,f0-ff:0000-7fff mask=0x8000 -board id:2A3M-01#R - :rom - : map address=00-3f,80-bf:8000-ffff mask=0x8000 - :ram - : map address=70-7d,f0-ff:0000-7fff mask=0x8000 +board: 2A3M-01#R + rom + map address=00-3f,80-bf:8000-ffff mask=0x8000 + ram + map address=70-7d,f0-ff:0000-7fff mask=0x8000 -board id:2A3M-(01,11,20) - :rom - : map address=00-7d,80-ff:8000-ffff mask=0x8000 - :ram - : map address=70-7d,f0-ff:0000-7fff mask=0x8000 +board: 2A3M-(01,11,20) + rom + map address=00-7d,80-ff:8000-ffff mask=0x8000 + ram + map address=70-7d,f0-ff:0000-7fff mask=0x8000 -board id:2A5M-01 - :rom - : map address=00-7d,80-ff:8000-ffff mask=0x8000 - :ram - : map address=70-7d,f0-ff:0000-7fff mask=0x8000 +board: 2A5M-01 + rom + map address=00-7d,80-ff:8000-ffff mask=0x8000 + ram + map address=70-7d,f0-ff:0000-7fff mask=0x8000 -board id:2B3B-01 - :rom - : map address=00-3f,80-bf:8000-ffff mask=0x8000 - :ram - : map address=70-7d,f0-ff:0000-7fff mask=0x8000 - :necdsp model=uPD7725 frequency=8000000 - : map address=60-6f,e0-ef:0000-7fff mask=0x3fff - : prom - : drom - : dram +board: 2B3B-01 + rom + map address=00-3f,80-bf:8000-ffff mask=0x8000 + ram + map address=70-7d,f0-ff:0000-7fff mask=0x8000 + necdsp model=uPD7725 frequency=8000000 + map address=60-6f,e0-ef:0000-7fff mask=0x3fff + prom + drom + dram -board id:2DC0N-01 - :hitachidsp model=HG51B169 frequency=20000000 - : map address=00-3f,80-bf:6c00-6fff,7c00-7fff - : rom - : map address=00-3f,80-bf:8000-ffff mask=0x8000 - : ram - : map address=70-77:0000-7fff mask=0x8000 - : drom - : dram - : map address=00-3f,80-bf:6000-6bff,7000-7bff mask=0xf000 +board: 2DC0N-01 + hitachidsp model=HG51B169 frequency=20000000 + map address=00-3f,80-bf:6c00-6fff,7c00-7fff + rom + map address=00-3f,80-bf:8000-ffff mask=0x8000 + ram + map address=70-77:0000-7fff mask=0x8000 + drom + dram + map address=00-3f,80-bf:6000-6bff,7000-7bff mask=0xf000 -board id:2E3M-01 - :rom - : map address=00-3f,80-bf:8000-ffff mask=0x8000 - :obc1 - : map address=00-3f,80-bf:6000-7fff mask=0xe000 - : map address=70-71,f0-f1:6000-7fff,e000-ffff mask=0xe000 - : ram +board: 2E3M-01 + rom + map address=00-3f,80-bf:8000-ffff mask=0x8000 + obc1 + map address=00-3f,80-bf:6000-7fff mask=0xe000 + map address=70-71,f0-f1:6000-7fff,e000-ffff mask=0xe000 + ram -board id:2J0N-(01,10,11,20) - :rom - : map address=00-3f,80-bf:8000-ffff - : map address=40-7d,c0-ff:0000-ffff +board: 2J0N-(01,10,11,20) + rom + map address=00-3f,80-bf:8000-ffff + map address=40-7d,c0-ff:0000-ffff -board id:2J3M-(01,11,20) - :rom - : map address=00-3f,80-bf:8000-ffff - : map address=40-7d,c0-ff:0000-ffff - :ram - : map address=10-1f,30-3f,90-9f,b0-bf:6000-7fff mask=0xe000 +board: 2J3M-(01,11,20) + rom + map address=00-3f,80-bf:8000-ffff + map address=40-7d,c0-ff:0000-ffff + ram + map address=10-1f,30-3f,90-9f,b0-bf:6000-7fff mask=0xe000 -board id:2J5M-01 - :rom - : map address=00-3f,80-bf:8000-ffff - : map address=40-7d,c0-ff:0000-ffff - :ram - : map address=10-1f,30-3f,90-9f,b0-bf:6000-7fff mask=0xe000 +board: 2J5M-01 + rom + map address=00-3f,80-bf:8000-ffff + map address=40-7d,c0-ff:0000-ffff + ram + map address=10-1f,30-3f,90-9f,b0-bf:6000-7fff mask=0xe000 -board id:3J0N-01 - :rom - : map address=00-2f,80-af:8000-ffff - : map address=40-6f,c0-ef:0000-ffff +board: 3J0N-01 + rom + map address=00-2f,80-af:8000-ffff + map address=40-6f,c0-ef:0000-ffff -board id:BA0N-(01,10) - :rom - : map address=00-7d,80-ff:8000-ffff mask=0x8000 - : map address=40-7d,c0-ff:0000-7fff mask=0x8000 +board: BA0N-(01,10) + rom + map address=00-7d,80-ff:8000-ffff mask=0x8000 + map address=40-7d,c0-ff:0000-7fff mask=0x8000 -board id:BA1M-01 - :rom - : map address=00-7d,80-ff:8000-ffff mask=0x8000 - :ram - : map address=70-7d,f0-ff:0000-7fff mask=0x8000 +board: BA1M-01 + rom + map address=00-7d,80-ff:8000-ffff mask=0x8000 + ram + map address=70-7d,f0-ff:0000-7fff mask=0x8000 -board id:BA3M-(01,10) - :rom - : map address=00-7d,80-ff:8000-ffff mask=0x8000 - :ram - : map address=70-7d,f0-ff:0000-7fff mask=0x8000 +board: BA3M-(01,10) + rom + map address=00-7d,80-ff:8000-ffff mask=0x8000 + ram + map address=70-7d,f0-ff:0000-7fff mask=0x8000 -board id:BJ0N-(01,20) - :rom - : map address=00-3f,80-bf:8000-ffff - : map address=40-7d,c0-ff:0000-ffff +board: BJ0N-(01,20) + rom + map address=00-3f,80-bf:8000-ffff + map address=40-7d,c0-ff:0000-ffff -board id:BJ1M-10 - :rom - : map address=00-3f,80-bf:8000-ffff - : map address=40-7d,c0-ff:0000-ffff - :ram - : map address=20-3f,a0-bf:6000-7fff mask=0xe000 +board: BJ1M-10 + rom + map address=00-3f,80-bf:8000-ffff + map address=40-7d,c0-ff:0000-ffff + ram + map address=20-3f,a0-bf:6000-7fff mask=0xe000 -board id:BJ3M-10 - :rom - : map address=00-3f,80-bf:8000-ffff - : map address=40-7d,c0-ff:0000-ffff - :ram - : map address=20-3f,a0-bf:6000-7fff mask=0xe000 +board: BJ3M-10 + rom + map address=00-3f,80-bf:8000-ffff + map address=40-7d,c0-ff:0000-ffff + ram + map address=20-3f,a0-bf:6000-7fff mask=0xe000 -board id:SGB-R-10 - :rom - : map address=00-7d,80-ff:8000-ffff mask=0x8000 - : map address=40-7d,c0-ff:0000-7fff mask=0x8000 - :icd2 revision=1 - : map address=00-3f,80-bf:6000-67ff,7000-7fff - : rom +board: SGB-R-10 + rom + map address=00-7d,80-ff:8000-ffff mask=0x8000 + map address=40-7d,c0-ff:0000-7fff mask=0x8000 + icd2 revision=1 + map address=00-3f,80-bf:6000-67ff,7000-7fff + rom -board id:YA0N-01 - :rom - : map address=00-7d,80-ff:8000-ffff mask=0x8000 - : map address=40-7d,c0-ff:0000-7fff mask=0x8000 +board: YA0N-01 + rom + map address=00-7d,80-ff:8000-ffff mask=0x8000 + map address=40-7d,c0-ff:0000-7fff mask=0x8000 -board id:YJ0N-01 - :rom - : map address=00-3f,80-bf:8000-ffff - : map address=40-7d,c0-ff:0000-ffff +board: YJ0N-01 + rom + map address=00-3f,80-bf:8000-ffff + map address=40-7d,c0-ff:0000-ffff diff --git a/icarus/core/super-famicom.cpp b/icarus/core/super-famicom.cpp index 0ec64b4d..251c7b37 100644 --- a/icarus/core/super-famicom.cpp +++ b/icarus/core/super-famicom.cpp @@ -11,10 +11,9 @@ auto Icarus::superFamicomManifest(string location) -> string { } auto Icarus::superFamicomManifest(vector& buffer, string location) -> string { - string markup; string digest = Hash::SHA256(buffer).digest(); - if(settings["icarus/UseDatabase"].boolean() && !markup) { + if(settings["icarus/UseDatabase"].boolean()) { for(auto game : database.superFamicom.find("game")) { if(game["sha256"].text() == digest) { return BML::serialize(game); @@ -22,20 +21,21 @@ auto Icarus::superFamicomManifest(vector& buffer, string location) -> s } } - if(settings["icarus/UseHeuristics"].boolean() && !markup) { + if(settings["icarus/UseHeuristics"].boolean()) { bool hasMSU1 = exists({location, "msu1.rom"}); - SuperFamicomCartridge cartridge{buffer.data(), buffer.size(), hasMSU1}; - if(markup = cartridge.markup) { - markup.append("\n"); - markup.append("game\n"); - markup.append(" sha256: ", digest, "\n"); - markup.append(" region: ", cartridge.region == SuperFamicomCartridge::Region::NTSC ? "NTSC" : "PAL", "\n"); - markup.append(" label: ", Location::prefix(location), "\n"); - markup.append(" note: ", "heuristically generated by icarus\n"); + Heuristics::SuperFamicom game{buffer.data(), buffer.size(), hasMSU1}; + if(string manifest = game.manifest()) { +// markup.append("\n"); +// markup.append("game\n"); +// markup.append(" sha256: ", digest, "\n"); +// markup.append(" region: ", cartridge.region == SuperFamicomCartridge::Region::NTSC ? "NTSC" : "PAL", "\n"); +// markup.append(" label: ", Location::prefix(location), "\n"); +// markup.append(" note: ", "heuristically generated by icarus\n"); + return manifest; } } - return markup; + return ""; } auto Icarus::superFamicomManifestScan(vector& roms, Markup::Node node) -> void { diff --git a/icarus/heuristics/super-famicom.cpp b/icarus/heuristics/super-famicom.cpp index 2366e8c0..4e5e6093 100644 --- a/icarus/heuristics/super-famicom.cpp +++ b/icarus/heuristics/super-famicom.cpp @@ -1,856 +1,373 @@ -struct SuperFamicomCartridge { - SuperFamicomCartridge(const uint8_t* data, uint size, bool has_msu1 = false); +namespace Heuristics { - string markup; +struct SuperFamicom { + SuperFamicom(const uint8_t* data, uint size, bool hasMSU1 = false); + explicit operator bool() const; -//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; + auto manifest() const -> string; + auto memory(string type, uint size, string name) const -> string; + auto sha256() const -> string; + auto region() const -> string; + auto revision() const -> string; + auto board() const -> string; + auto label() const -> string; + auto serial() const -> string; + auto ramSize() const -> uint; + auto expansionRamSize() const -> 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, - }; +private: + auto scoreHeader(uint address) -> uint; - 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; + const uint8_t* data = nullptr; + uint size = 0; + uint headerAddress = 0; + bool hasMSU1 = false; }; -SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t* data, uint size, bool has_msu1) { - //skip copier header - if((size & 0x7fff) == 512) data += 512, size -= 512; +SuperFamicom::SuperFamicom(const uint8_t* data, uint size, bool hasMSU1) : data(data), size(size), hasMSU1(hasMSU1) { + if((size & 0x7fff) == 512) data += 512, size -= 512; //skip copier header (if present) + if(size < 0x8000) return; //ignore images too small to be valid - //ignore images too small to be valid - if(size < 0x8000) return; + uint scoreLo = scoreHeader( 0x7fb0); + uint scoreHi = scoreHeader( 0xffb0); + uint scoreEx = scoreHeader(0x40ffb0); + if(scoreEx) scoreEx += 4; - 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" - ); - } + if(scoreLo >= scoreHi && scoreLo >= scoreEx) headerAddress = 0x7fb0; + else if(scoreHi >= scoreEx) headerAddress = 0xffb0; + else headerAddress = 0x40ffb0; } -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; - } - } +SuperFamicom::operator bool() const { + return headerAddress; +} - 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; +auto SuperFamicom::manifest() const -> string { + if(!operator bool()) return "error\n"; - const uint16_t complement = data[index + Complement] | data[index + Complement + 1] << 8; - const uint16_t checksum = data[index + Checksum] | data[index + Checksum + 1] << 8; + string output; + output.append("game\n"); + output.append(" sha256: ", sha256(), "\n"); + output.append(" region: ", region(), "\n"); + output.append(" revision: ", revision(), "\n"); + output.append(" board: ", board(), "\n"); + output.append(" label: ", label(), "\n"); - 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) + auto board = this->board(); - //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 - } + if(board.beginsWith("CX4-")) { + output.append(memory("ROM", size - 0xc00, "program.rom")); + output.append(memory("ROM", 0xc00, "cx4.data.rom")); + } else if(board.beginsWith("DSP-")) { + output.append(memory("ROM", size - 0x2000, "program.rom")); + output.append(memory("ROM", 0x1800, "dsp.program.rom")); + output.append(memory("ROM", 0x800, "dsp.data.rom")); + } else if(board.beginsWith("SETA-")) { + output.append(memory("ROM", size - 0x28000, "program.rom")); + output.append(memory("ROM", 0x20000, "seta.program.rom")); + output.append(memory("ROM", 0x8000, "seta.data.rom")); + } else if(board.beginsWith("SGB-")) { + output.append(memory("ROM", size - 0x100, "program.rom")); + output.append(memory("ROM", 0x100, "sgb.boot.rom")); + } else if(board.beginsWith("SPC7110-")) { + output.append(memory("ROM", 0x100000, "program.rom")); + output.append(memory("ROM", size - 0x100000, "data.rom")); + } else if(board.beginsWith("ST-")) { + output.append(memory("ROM", size - 0xd000, "program.rom")); + output.append(memory("ROM", 0xc000, "st.program.rom")); + output.append(memory("ROM", 0x1000, "st.data.rom")); } 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; - } + output.append(memory("ROM", size, "program.rom")); } - 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(auto size = ramSize()) { + auto type = board.endsWith("-NVRAM") ? "NVRAM" : "RAM"; + output.append(memory(type, size, "save.ram")); } - if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) { - has_sa1 = true; - type = Type::SA1; + if(auto size = expansionRamSize()) { + auto type = board.endsWith("-NVRAM") ? "NVRAM" : "RAM"; + output.append(memory(type, size, "expansion.ram")); } - if(mapperid == 0x35 && rom_type == 0x55) { - has_sharprtc = true; + if(board.beginsWith("BSX-")) { + output.append(memory("NVRAM", 0x80000, "download.ram")); + } else if(board.beginsWith("CX4-")) { + output.append(memory("RAM", 0, "save.ram")); + output.append(memory("RAM", 0xc00, "cx4.data.ram")); + } else if(board.beginsWith("DSP-")) { + output.append(memory("RAM", 0x200, "dsp.data.ram")); + } else if(board.beginsWith("RTC-")) { + output.append(memory("NVRAM", 0x10, "rtc.ram")); + } else if(board.beginsWith("SA1-")) { + output.append(memory("RAM", 0x800, "internal.ram")); + } else if(board.beginsWith("SETA-")) { + output.append(memory("NVRAM", 0x4000, "seta.save.ram")); + } else if(board.beginsWith("SPC7110-RTC-")) { + output.append(memory("NVRAM", 0x10, "rtc.ram")); + } else if(board.beginsWith("ST-")) { + output.append(memory("NVRAM", 0x1000, "st.save.ram")); } - if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { - has_sdd1 = true; + if(hasMSU1) { + output.append(" msu1\n"); } - 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; - } + return output; } -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 +auto SuperFamicom::memory(string type, uint size, string name) const -> string { + string output; + output.append(" memory\n"); + output.append(" type: ", type, "\n"); + output.append(" size: 0x", hex(size), "\n"); + output.append(" name: ", name, "\n"); + return output; +} - if(score_lo >= score_hi && score_lo >= score_ex) { - return 0x007fc0; - } else if(score_hi >= score_ex) { - return 0x00ffc0; +auto SuperFamicom::sha256() const -> string { + return Hash::SHA256(data, size).digest(); +} + +auto SuperFamicom::region() const -> string { + string region; + + char A = data[headerAddress + 0x02]; //game type + char B = data[headerAddress + 0x03]; //game code + char C = data[headerAddress + 0x04]; //game code + char D = data[headerAddress + 0x05]; //region code (new; sometimes ambiguous) + auto E = data[headerAddress + 0x29]; //region code (old) + + auto valid = [](char n) { return (n >= '0' && n <= '9') || (n >= 'A' && n <= 'Z'); }; + if(data[headerAddress + 0x2a] == 0x33 && valid(A) && valid(B) & valid(C) & valid(D)) { + string code{A, B, C, D}; + if(D == 'B') region = {"SNS-", code, "-BRA"}; + if(D == 'C') region = {"SNSN-", code, "-ROC"}; + if(D == 'D') region = {"SNSP-", code, "-NOE"}; + if(D == 'E') region = {"SNS-", code, "-USA"}; + if(D == 'F') region = {"SNSP-", code, "-FRA"}; + if(D == 'H') region = {"SNSP-", code, "-HOL"}; + if(D == 'I') region = {"SNSP-", code, "-ITA"}; + if(D == 'J') region = {"SHVC-", code, "-JPN"}; + if(D == 'K') region = {"SNSN-", code, "-KOR"}; + if(D == 'N') region = {"SNS-", code, "-CAN"}; + if(D == 'P') region = {"SNSP-", code, "-EUR"}; + if(D == 'S') region = {"SNSP-", code, "-ESP"}; + if(D == 'U') region = {"SNSP-", code, "-AUS"}; + if(D == 'W') region = {"SNSP-", code, "-SCN"}; + } + + if(!region) { + if(E == 0x00) region = {"SHVC-\?\?-JPN"}; //trigraphs: why is this still a thing? + if(E == 0x01) region = { "SNS-\?\?-USA"}; + if(E == 0x02) region = {"SNSP-\?\?-EUR"}; + if(E == 0x03) region = {"SNSP-\?\?-SCN"}; + if(E == 0x06) region = {"SNSP-\?\?-FRA"}; + if(E == 0x07) region = {"SNSP-\?\?-HOL"}; + if(E == 0x08) region = {"SNSP-\?\?-ESP"}; + if(E == 0x09) region = {"SNSP-\?\?-NOE"}; + if(E == 0x0a) region = {"SNSP-\?\?-ITA"}; + if(E == 0x0b) region = {"SNSP-\?\?-ROC"}; + if(E == 0x0d) region = {"SNSP-\?\?-KOR"}; + if(E == 0x0f) region = { "SNS-\?\?-CAN"}; + if(E == 0x10) region = { "SNS-\?\?-BRA"}; + if(E == 0x11) region = {"SNSP-\?\?-AUS"}; + } + + return region ? region : "NTSC"; +} + +auto SuperFamicom::revision() const -> string { + string revision; + + char A = data[headerAddress + 0x02]; //game type + char B = data[headerAddress + 0x03]; //game code + char C = data[headerAddress + 0x04]; //game code + char D = data[headerAddress + 0x05]; //region code (new; sometimes ambiguous) + auto E = data[headerAddress + 0x29]; //region code (old) + uint F = data[headerAddress + 0x2b]; //revision code + + auto valid = [](char n) { return (n >= '0' && n <= '9') || (n >= 'A' && n <= 'Z'); }; + if(data[headerAddress + 0x2a] == 0x33 && valid(A) && valid(B) & valid(C) & valid(D)) { + string code{A, B, C, D}; + if(D == 'B') revision = {"SNS-", code, "-", F}; + if(D == 'C') revision = {"SNSN-", code, "-", F}; + if(D == 'D') revision = {"SNSP-", code, "-", F}; + if(D == 'E') revision = {"SNS-", code, "-", F}; + if(D == 'F') revision = {"SNSP-", code, "-", F}; + if(D == 'H') revision = {"SNSP-", code, "-", F}; + if(D == 'I') revision = {"SNSP-", code, "-", F}; + if(D == 'J') revision = {"SHVC-", code, "-", F}; + if(D == 'K') revision = {"SNSN-", code, "-", F}; + if(D == 'N') revision = {"SNS-", code, "-", F}; + if(D == 'P') revision = {"SNSP-", code, "-", F}; + if(D == 'S') revision = {"SNSP-", code, "-", F}; + if(D == 'U') revision = {"SNSP-", code, "-", F}; + if(D == 'W') revision = {"SNSP-", code, "-", F}; + } + + if(!revision) { + if(E == 0x00) revision = {"SHVC-\?\?-", F}; + if(E == 0x01) revision = { "SNS-\?\?-", F}; + if(E == 0x02) revision = {"SNSP-\?\?-", F}; + if(E == 0x03) revision = {"SSWE-\?\?-", F}; + if(E == 0x06) revision = {"SFRA-\?\?-", F}; + if(E == 0x07) revision = {"SHOL-\?\?-", F}; + if(E == 0x08) revision = {"SESP-\?\?-", F}; + if(E == 0x09) revision = {"SFRG-\?\?-", F}; + if(E == 0x0a) revision = {"SITA-\?\?-", F}; + if(E == 0x0b) revision = {"SSCN-\?\?-", F}; + if(E == 0x0d) revision = {"SKOR-\?\?-", F}; + if(E == 0x0f) revision = { "SNS-\?\?-", F}; + if(E == 0x10) revision = {"SBRA-\?\?-", F}; + if(E == 0x11) revision = {"SNSP-\?\?-", F}; + } + + return revision ? revision : string{"1.", F}; +} + +auto SuperFamicom::board() const -> string { + auto mapModeLo = data[headerAddress + 0x25] & 15; + auto cartridgeType = data[headerAddress + 0x26]; + auto cartridgeTypeLo = cartridgeType & 15; + auto cartridgeTypeHi = cartridgeType >> 4; + auto cartridgeSubType = data[headerAddress + 0x2f]; + + string board; + + string mode; + if(mapModeLo == 0x0) mode = "LOROM-"; + if(mapModeLo == 0x1) mode = "HIROM-"; + if(mapModeLo == 0x2) mode = "SDD1-"; + if(mapModeLo == 0x3) mode = "SA1-"; + if(mapModeLo == 0x5) mode = "EXHIROM-"; + if(mapModeLo == 0xa) mode = "SPC7110-"; + + if(serial() == "A9PJ") { + board.append("ST-"); + } else if(serial().match("Z\?\?J")) { + board.append("BS-", mode); + } else if(cartridgeTypeLo >= 0x3) { + if(cartridgeTypeHi == 0x0) board.append("DSP-", mode); + if(cartridgeTypeHi == 0x1) board.append("SUPERFX-"); + if(cartridgeTypeHi == 0x2) board.append("OBC1-", mode); + if(cartridgeTypeHi == 0x3) board.append("SA1-"); + if(cartridgeTypeHi == 0x4) board.append("SDD1-"); + if(cartridgeTypeHi == 0x5) board.append("RTC-", mode); + if(cartridgeTypeHi == 0xe && cartridgeTypeLo == 0x3) board.append("SGB-"); + if(cartridgeTypeHi == 0xe && cartridgeTypeLo == 0x5) board.append("BSX-"); + if(cartridgeTypeHi == 0xf && cartridgeSubType == 0x00 && cartridgeTypeLo == 0x5) board.append("SPC7110-"); + if(cartridgeTypeHi == 0xf && cartridgeSubType == 0x00 && cartridgeTypeLo == 0x9) board.append("SPC7110-RTC-"); + if(cartridgeTypeHi == 0xf && cartridgeSubType == 0x01) board.append("ST-"); + if(cartridgeTypeHi == 0xf && cartridgeSubType == 0x02) board.append("SETA-"); + if(cartridgeTypeHi == 0xf && cartridgeSubType == 0x10) board.append("CX4-"); } else { - return 0x40ffc0; + board.append(mode); } + + if(cartridgeTypeLo == 0x1 || cartridgeTypeLo == 0x4) board.append("RAM-"); + if(cartridgeTypeLo == 0x2 || cartridgeTypeLo == 0x5) board.append("NVRAM-"); + if(cartridgeTypeLo == 0x6) board.append("BATTERY-"); + + return board.trimRight("-", 1L); } -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? +auto SuperFamicom::label() const -> string { + string label; + auto append = [&](char c) -> void { + if(c >= 0x20 && c <= 0x7e) label.append(c); //ASCII + //todo: convert Shift-JIS half-width katakana to UTF-8 here + else label.append("?"); + }; + for(uint n : range(0x15)) append(data[headerAddress + 0x10 + n]); + return label.strip(); +} + +auto SuperFamicom::serial() const -> string { + char A = data[headerAddress + 0x02]; //game type + char B = data[headerAddress + 0x03]; //game code + char C = data[headerAddress + 0x04]; //game code + char D = data[headerAddress + 0x05]; //region code (new; sometimes ambiguous) + + auto valid = [](char n) { return (n >= '0' && n <= '9') || (n >= 'A' && n <= 'Z'); }; + if(data[headerAddress + 0x2a] == 0x33 && valid(A) && valid(B) & valid(C) & valid(D)) { + return {A, B, C, D}; + } + + return ""; +} + +auto SuperFamicom::ramSize() const -> uint { + auto ramSize = data[headerAddress + 0x28] & 7; + if(ramSize) return 1024 << ramSize; + return 0; +} + +auto SuperFamicom::expansionRamSize() const -> uint { + if(data[headerAddress + 0x2a] != 0x33) return 0; + auto ramSize = data[headerAddress + 0x0d] & 7; + if(ramSize) return 1024 << ramSize; + return 0; +} + +auto SuperFamicom::scoreHeader(uint address) -> uint { int score = 0; + if(size < address + 0x50) return score; - 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 mapMode = data[address + 0x25] & ~0x10; //ignore FastROM bit + uint16_t complement = data[address + 0x2c] << 0 | data[address + 0x2d] << 8; + uint16_t checksum = data[address + 0x2e] << 0 | data[address + 0x2f] << 8; + uint16_t resetVector = data[address + 0x4c] << 0 | data[address + 0x4d] << 8; + if(resetVector < 0x8000) return score; //$00:0000-7fff is never ROM data - 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. + uint8_t opcode = data[(address & ~0x7fff) | (resetVector & 0x7fff)]; //first instruction executed //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 + if(opcode == 0x78 //sei + || opcode == 0x18 //clc (clc; xce) + || opcode == 0x38 //sec (sec; xce) + || opcode == 0x9c //stz $nnnn (stz $4200) + || opcode == 0x4c //jmp $nnnn + || opcode == 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 + if(opcode == 0xc2 //rep #$nn + || opcode == 0xe2 //sep #$nn + || opcode == 0xad //lda $nnnn + || opcode == 0xae //ldx $nnnn + || opcode == 0xac //ldy $nnnn + || opcode == 0xaf //lda $nnnnnn + || opcode == 0xa9 //lda #$nn + || opcode == 0xa2 //ldx #$nn + || opcode == 0xa0 //ldy #$nn + || opcode == 0x20 //jsr $nnnn + || opcode == 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 + if(opcode == 0x40 //rti + || opcode == 0x60 //rts + || opcode == 0x6b //rtl + || opcode == 0xcd //cmp $nnnn + || opcode == 0xec //cpx $nnnn + || opcode == 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 + if(opcode == 0x00 //brk #$nn + || opcode == 0x02 //cop #$nn + || opcode == 0xdb //stp + || opcode == 0x42 //wdm + || opcode == 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. + if(checksum + complement == 0xffff) score += 4; - //a valid checksum is the biggest indicator of a valid header. - if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4; + if(address == 0x7fb0 && mapMode == 0x20) score += 2; + if(address == 0xffb0 && mapMode == 0x21) score += 2; + + return max(0, score); +} - 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; } diff --git a/icarus/heuristics/super-famicom.old.cpp b/icarus/heuristics/super-famicom.old.cpp new file mode 100644 index 00000000..2366e8c0 --- /dev/null +++ b/icarus/heuristics/super-famicom.old.cpp @@ -0,0 +1,856 @@ +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; +}