From 3d8be92550d91f3bb432be870d63904a01da6352 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 5 Feb 2018 20:58:02 +1100 Subject: [PATCH] Update to v106r3 release. byuu says: Changelog: - Super Famicom: update to newer board markup syntax - Super Famicom: update all mapped ROMs to be write-protected - errata: SPC7110 set ram.writeProtect(true), I'll fix it in the next WIP - icarus: rewrote the Super Famicom heuristics module from scratch Instead of icarus heuristics generating higan-specific mappings, it now generates generic board IDs that can be used by any emulator. I had originally planned to print out real PCB ID codes here, but these board mappings are meant to be more generic, and I don't want them to look real. The pseudo-codes are easy to parse, for example: `DSP-LOROM-NVRAM` for Super Mario Kart, `SUPERFX-RAM` for Doom. I'm going to make a `Boards (Generic).bml` file that will contain mapping definitions for every board. Until this is done, any games not in the SNES preservation database will fail to play because the mapping information is now missing. --- higan/emulator/emulator.hpp | 2 +- higan/sfc/cartridge/load.cpp | 5 +- higan/sfc/coprocessor/event/event.cpp | 6 + .../sfc/coprocessor/hitachidsp/hitachidsp.cpp | 3 + higan/sfc/coprocessor/mcc/mcc.cpp | 3 + higan/sfc/coprocessor/obc1/obc1.cpp | 2 + higan/sfc/coprocessor/sa1/sa1.cpp | 4 + higan/sfc/coprocessor/sdd1/sdd1.cpp | 3 + higan/sfc/coprocessor/spc7110/spc7110.cpp | 4 + higan/sfc/coprocessor/superfx/superfx.cpp | 3 + higan/sfc/slot/sufamiturbo/sufamiturbo.cpp | 5 + higan/sfc/slot/sufamiturbo/sufamiturbo.hpp | 1 + higan/sfc/system/system.cpp | 1 + higan/systems/Super Famicom.sys/boards.bml | 707 +++++------ icarus/core/super-famicom.cpp | 24 +- icarus/heuristics/super-famicom.cpp | 1127 +++++------------ icarus/heuristics/super-famicom.old.cpp | 856 +++++++++++++ 17 files changed, 1583 insertions(+), 1173 deletions(-) create mode 100644 icarus/heuristics/super-famicom.old.cpp 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; +}