Skip to content

Commit

Permalink
gba: handle ROM mirroring for Famicom Mini / Classic NES Series (ares…
Browse files Browse the repository at this point in the history
…-emulator#1456)

The 1MiB ROM chips used in most Famicom Mini carts and all Classic NES
series carts are mirrored, unlike with the larger ROM chips other carts
used. This PR implements the ROM mirroring behaviour and adds detection
for the affected games.

Closes ares-emulator#893
  • Loading branch information
png183 authored Apr 21, 2024
1 parent 74deb93 commit eaab017
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 3 deletions.
1 change: 1 addition & 0 deletions ares/gba/cartridge/cartridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ auto Cartridge::connect() -> void {
if(auto fp = pak->read("program.rom")) {
mrom.size = min(32_MiB, fp->size());
fp->read({mrom.data, mrom.size});
mrom.mirror = pak->attribute("mirror").boolean();
}

if(auto fp = pak->read("save.ram")) {
Expand Down
2 changes: 2 additions & 0 deletions ares/gba/cartridge/memory.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
struct MROM {
//mrom.cpp
auto read(u32 mode, n32 address) -> n32;
auto readBus(u32 mode, n32 address) -> n16;
auto write(u32 mode, n32 address, n32 word) -> void;

//serialization.cpp
Expand All @@ -9,6 +10,7 @@ struct MROM {
n8* data = nullptr;
u32 size;
u32 mask;
bool mirror;
} mrom;

struct SRAM {
Expand Down
14 changes: 11 additions & 3 deletions ares/gba/cartridge/mrom.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
auto Cartridge::MROM::read(u32 mode, n32 address) -> n32 {
if(mode & Word) {
n32 word = 0;
word |= read(mode & ~Word | Half, (address & ~3) + 0) << 0;
word |= read(mode & ~Word | Half, (address & ~3) + 2) << 16;
word |= readBus(mode & ~Word | Half, (address & ~3) + 0) << 0;
word |= readBus(mode & ~Word | Half, (address & ~3) + 2) << 16;
return word;
}

return readBus(mode, address);
}

auto Cartridge::MROM::readBus(u32 mode, n32 address) -> n16 {
address &= 0x01ff'ffff;
if(address >= size) return (n16)(address >> 1);

if(address >= size) {
if(!mirror) return (n16)(address >> 1);
address &= size - 1;
}

if(mode & Half) address &= ~1;
auto p = data + address;
Expand Down
57 changes: 57 additions & 0 deletions mia/medium/game-boy-advance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ auto GameBoyAdvance::load(string location) -> bool {
}
}

bool mirror = false;
if(auto node = document["game/board/memory(type=ROM,mirror=true)"]) {
mirror = true;
}
pak->setAttribute("mirror", mirror);

return true;
}

Expand Down Expand Up @@ -67,6 +73,56 @@ auto GameBoyAdvance::analyze(vector<u8>& rom) -> string {
"FLASH1M_V",
};

vector<string> mirrorCodes = {
"FBME", //Classic NES Series - Bomberman (USA, Europe)
"FADE", //Classic NES Series - Castlevania (USA)
"FDKE", //Classic NES Series - Donkey Kong (USA, Europe)
"FEBE", //Classic NES Series - Excitebike (USA, Europe)
"FICE", //Classic NES Series - Ice Climber (USA, Europe)
"FSME", //Classic NES Series - Super Mario Bros. (USA, Europe)
"FZLE", //Classic NES Series - The Legend of Zelda (USA, Europe)
"FDME", //Classic NES Series - Dr. Mario (USA, Europe)
"FMRE", //Classic NES Series - Metroid (USA, Europe)
"FP7E", //Classic NES Series - Pac-Man (USA, Europe)
"FXVE", //Classic NES Series - Xevious (USA, Europe)
"FLBE", //Classic NES Series - Zelda II - The Adventure of Link (USA, Europe)
"FSRJ", //Famicom Mini - Dai-2-ji Super Robot Taisen (Japan) (Promo)
"FGZJ", //Famicom Mini - Kidou Senshi Z Gundam - Hot Scramble (Japan) (Promo)
"FSMJ", //Famicom Mini 01 - Super Mario Bros. (Japan) (En) (Rev 1)
"FDKJ", //Famicom Mini 02 - Donkey Kong (Japan) (En)
"FICJ", //Famicom Mini 03 - Ice Climber (Japan) (En)
"FEBJ", //Famicom Mini 04 - Excitebike (Japan) (En)
"FZLJ", //Famicom Mini 05 - Zelda no Densetsu 1 - The Hyrule Fantasy (Japan)
"FPMJ", //Famicom Mini 06 - Pac-Man (Japan) (En)
"FXVJ", //Famicom Mini 07 - Xevious (Japan) (En)
"FMPJ", //Famicom Mini 08 - Mappy (Japan) (En)
"FBMJ", //Famicom Mini 09 - Bomberman (Japan) (En)
"FSOJ", //Famicom Mini 10 - Star Soldier (Japan) (En)
"FMBJ", //Famicom Mini 11 - Mario Bros. (Japan)
"FCLJ", //Famicom Mini 12 - Clu Clu Land (Japan)
"FBFJ", //Famicom Mini 13 - Balloon Fight (Japan)
"FWCJ", //Famicom Mini 14 - Wrecking Crew (Japan)
"FDMJ", //Famicom Mini 15 - Dr. Mario (Japan)
"FDDJ", //Famicom Mini 16 - Dig Dug (Japan)
"FTBJ", //Famicom Mini 17 - Takahashi Meijin no Bouken-jima (Japan)
"FMKJ", //Famicom Mini 18 - Makaimura (Japan)
"FTWJ", //Famicom Mini 19 - Twin Bee (Japan)
"FGGJ", //Famicom Mini 20 - Ganbare Goemon! - Karakuri Douchuu (Japan)
"FM2J", //Famicom Mini 21 - Super Mario Bros. 2 (Japan)
"FADJ", //Famicom Mini 29 - Akumajou Dracula (Japan)
"FSDJ", //Famicom Mini 30 - SD Gundam World - Gachapon Senshi Scramble Wars (Japan)
};

string gameCode = "????";
memory::copy(&gameCode, &rom[0xac], 4);
string mirror = "false";

for(auto& code : mirrorCodes) {
if(!memory::compare(&gameCode, code.data(), code.size())) {
mirror = "true";
}
}

vector<string> list;
for(auto& identifier : identifiers) {
for(s32 n : range(rom.size() - 16)) {
Expand All @@ -86,6 +142,7 @@ auto GameBoyAdvance::analyze(vector<u8>& rom) -> string {
s += " type: ROM\n";
s +={" size: 0x", hex(rom.size()), "\n"};
s += " content: Program\n";
s +={" mirror: ", mirror, "\n"};

if(list) {
if(list.first().beginsWith("SRAM_V") || list.first().beginsWith("SRAM_F_V")) {
Expand Down

0 comments on commit eaab017

Please sign in to comment.