Game Boy MMM01 emulation Author: byuu Date: 2010-01-04 Games that use MMM01: ===================== Momotarou Collection 2 Taito Variety Pack Word of warning: ================ There are bad dumps of the above games floating around. Verify correct dumps by examining in hex editor: Momotarou Collection 2: 00000 = Momotarou Collection 2 [menu] 08000 = Momotarou Dengeki 88000 = Momotarou Gaiden Taito Variety Pack: 00000 = Taito Variety Pack [menu] 08000 = Sagaia 28000 = Chase HQ 48000 = Bubble Bobble 68000 = Elevator Action The bad dumps place the menu at the very end of the ROM. If this is the case, header detection will fail. Assuming you have a bad dump, you will have to fix it yourself. Here is example code to fix Momotarou Collection 2: #include int main() { FILE *fp = fopen("momo.gb", "rb"); char g1[0x80000]; fread(g1, 1, 0x80000, fp); char g2[0x78000]; fread(g2, 1, 0x78000, fp); char g3[0x08000]; fread(g3, 1, 0x08000, fp); fclose(fp); fp = fopen("momo-out.gb", "wb"); fwrite(g3, 1, 0x08000, fp); fwrite(g1, 1, 0x80000, fp); fwrite(g2, 1, 0x78000, fp); fclose(fp); return 0; } Detecting MMM01: ================ switch(romdata[0x0147]) { ... case 0x0b: info.mapper = Mapper::MMM01; break; case 0x0c: info.mapper = Mapper::MMM01; info.ram = true; break; case 0x0d: info.mapper = Mapper::MMM01; info.ram = true; info.battery = true; break; ... } Emulating MMM01: ================ The MMM01 is a meta-mapper, it allows one to map 0000-3fff. Once a sub-game is mapped in, the MMM01 reverts to an ordinary mapper, allowing mapping of 4000-7fff and a000-bfff. Here is the source code for an MMM01 emulator. I cannot guarantee its completeness. struct MMM01 : MMIO { bool rom_mode; uint8 rom_base; bool ram_enable; uint8 rom_select; uint8 ram_select; uint8 mmio_read(uint16 addr); void mmio_write(uint16 addr, uint8 data); void power(); } mmm01; uint8 Cartridge::MMM01::mmio_read(uint16 addr) { if((addr & 0x8000) == 0x0000) { if(rom_mode == 0) return cartridge.rom_read(addr); } if((addr & 0xc000) == 0x0000) { return cartridge.rom_read(0x8000 + (rom_base << 14) + (addr & 0x3fff)); } if((addr & 0xc000) == 0x4000) { return cartridge.rom_read(0x8000 + (rom_base << 14) + (rom_select << 14) + (addr & 0x3fff)); } if((addr & 0xe000) == 0xa000) { if(ram_enable) return cartridge.ram_read((ram_select << 13) + (addr & 0x1fff)); return 0x00; } return 0x00; } void Cartridge::MMM01::mmio_write(uint16 addr, uint8 data) { if((addr & 0xe000) == 0x0000) { //0000-1fff if(rom_mode == 0) { rom_mode = 1; } else { ram_enable = (data & 0x0f) == 0x0a; } } if((addr & 0xe000) == 0x2000) { //2000-3fff if(rom_mode == 0) { rom_base = data & 0x3f; } else { rom_select = data; } } if((addr & 0xe000) == 0x4000) { //4000-5fff if(rom_mode == 1) { ram_select = data; } } if((addr & 0xe000) == 0x6000) { //6000-7fff //unknown purpose } if((addr & 0xe000) == 0xa000) { //a000-bfff if(ram_enable) cartridge.ram_write((ram_select << 13) + (addr & 0x1fff), data); } } void Cartridge::MMM01::power() { rom_mode = 0; rom_base = 0x00; ram_enable = false; rom_select = 0x01; ram_select = 0x00; }