From 9055778f3fc0e1c1757156635a49d02de558a1f2 Mon Sep 17 00:00:00 2001 From: "Benjamin C. Wiley Sittler" Date: Sun, 29 Jan 2023 21:20:37 -0800 Subject: [PATCH] Mapper `#28` MAPPER_GG_Super_12_in_1_FFFE for Super 12 in 1 Game Gear (Unl) It turns out this multicart is severable and also reversible. This produces three more multicart "ghost ROMs" using the same mapper and some or all of the same data: Just the first 2MB: Super 7 in 1 Game Gear [Super 12 in 1] (Unl).gg Just the last 2MB: Super 5 in 1 Game Gear [Super 12 in 1] (Unl).gg The last 2MB, followed by the first 2MB: Super 12 in 1 Game Gear [alt] (Unl).gg The menu is written such that it automatically adjusts to each of these subsets/layouts. Perhaps we will find physical carts containing these variations someday too? --- meka/compat.txt | 6 ++++- meka/meka.nam | 4 ++++ meka/srcs/machine.cpp | 18 ++++++++++++++ meka/srcs/mappers.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++ meka/srcs/mappers.h | 2 ++ meka/srcs/saves.cpp | 22 +++++++++++++++++ 6 files changed, 106 insertions(+), 1 deletion(-) diff --git a/meka/compat.txt b/meka/compat.txt index 1a98aee9..575cdca5 100644 --- a/meka/compat.txt +++ b/meka/compat.txt @@ -1404,6 +1404,10 @@ Street Hero [Proto 1] [SMS-GG] Ok Strider Returns Ok Striker Ok + Super 5 in 1 Game Gear [Mortal Kombat] [Super 12 in 1] *Ok + Super 7 in 1 Game Gear [Bare Knuckle 2] [Super 12 in 1] *Ok + Super 12 in 1 Game Gear [Mortal Kombat] *Ok + Super 12 in 1 Game Gear [Bare Knuckle 2] *Ok Super Battletank Ok Super Columns Ok Super Columns (JP) Ok @@ -1497,7 +1501,7 @@ Zoop (US) Ok Zoop [Proto] (US) Ok ----------------------------------------------------------------------------- - 517 games tested - 506 are "Ok" - Compatibility rate: 97.63% + 521 games tested - 510 are "Ok" - Compatibility rate: 97.89% ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- diff --git a/meka/meka.nam b/meka/meka.nam index 56e07012..89737e0e 100644 --- a/meka/meka.nam +++ b/meka/meka.nam @@ -1286,6 +1286,10 @@ GG 0b618409 85CFE82DBD812633 Streets of Rage II/NAME_US=Streets of Rage 2/NAM GG 6c395a69 BC45532F90B98FA5 Streets of Rage II [Proto]/NAME_US=Streets of Rage 2/NAME_JP=Bare Knuckle II/FLAGS=PROTO/COMMENT=Prototype version of the game. GG 1ebfa5ca 24A227CBB87248D6 Strider Returns (Journey from Darkness)/COUNTRY=US,EU/PRODUCT_NO=T-79048,79048,79048-50 GG b421c057 96BD12C62621B8D6 Striker/COUNTRY=EU/PRODUCT_NO=2551-50 +GG 0e098f92 F4782D31A464BB73 Super 5 in 1 Game Gear [Mortal Kombat] [Super 12 in 1]/EMU_MAPPER=28 +GG 55d04386 97D2BF32485053BB Super 7 in 1 Game Gear [Bare Knuckle 2] [Super 12 in 1]/EMU_MAPPER=28 +GG 2ff576fa 8B4AEC63ECB40E2E Super 12 in 1 Game Gear [Mortal Kombat]/EMU_MAPPER=28 +GG f56d3219 8B4AEC63ECB40E2E Super 12 in 1 Game Gear [Bare Knuckle 2]/EMU_MAPPER=28 GG 73d6745a 18CC99C9849C9901 Super Battletank/COUNTRY=US/PRODUCT_NO=1239 GG 8ba43af3 DAA4C785B7042952 Super Columns/COUNTRY=US,EU/PRODUCT_NO=2449,2449-50 GG 2a100717 E7260408CEC8EE63 Super Columns/COUNTRY=JP/PRODUCT_NO=G-3226 diff --git a/meka/srcs/machine.cpp b/meka/srcs/machine.cpp index 76391b00..fac7eb41 100644 --- a/meka/srcs/machine.cpp +++ b/meka/srcs/machine.cpp @@ -193,6 +193,9 @@ void Machine_Set_Handler_MemRW(void) case MAPPER_SMS_Korean_MD_FFFA: WrZ80 = WrZ80_NoHook = Write_Mapper_SMS_Korean_MD_FFFA; break; + case MAPPER_GG_Super_12_in_1_FFFE: + WrZ80 = WrZ80_NoHook = Write_Mapper_GG_Super_12_in_1_FFFE; + break; } } @@ -467,6 +470,21 @@ void Machine_Set_Mapping (void) g_machine.mapper_regs[2] = 1; break; + case MAPPER_GG_Super_12_in_1_FFFE: + Map_8k_ROM(0, 0 & tsms.Pages_Mask_8k); + Map_8k_ROM(1, 1 & tsms.Pages_Mask_8k); + Map_8k_ROM(2, 2 & tsms.Pages_Mask_8k); + Map_8k_ROM(3, 3 & tsms.Pages_Mask_8k); + Map_8k_ROM(4, 0 & tsms.Pages_Mask_8k); + Map_8k_ROM(5, 1 & tsms.Pages_Mask_8k); + Map_8k_RAM(6, 0); + Map_8k_RAM(7, 0); + g_machine.mapper_regs_count = 3; + for (int i = 0; i != MAPPER_REGS_MAX; i++) + g_machine.mapper_regs[i] = 0; + g_machine.mapper_regs[2] = 1; + break; + case MAPPER_SC3000_Survivors_Multicart: g_machine.mapper_regs_count = 1; for (int i = 0; i != MAPPER_REGS_MAX; i++) diff --git a/meka/srcs/mappers.cpp b/meka/srcs/mappers.cpp index 1fc1b3ad..e7c805f0 100644 --- a/meka/srcs/mappers.cpp +++ b/meka/srcs/mappers.cpp @@ -928,6 +928,61 @@ WRITE_FUNC(Write_Mapper_SMS_Korean_MD_FFFA) Write_Error(Addr, Value); } +// Mapper #28 +// +// Super 5 in 1 Game Gear [Mortal Kombat] [Super 12 in 1] +// Super 7 in 1 Game Gear [Bare Knuckle 2] [Super 12 in 1] +// Super 12 in 1 Game Gear [Mortal Kombat] (Unl) +// Super 12 in 1 Game Gear [Bare Knuckle 2] (Unl) +WRITE_FUNC (Write_Mapper_GG_Super_12_in_1_FFFE) +{ + if ((Addr == 0xFFFE) || (Addr == 0xFFFF)) // Configurable segment ----------------------------------------------- + { + int in_menu_mode = (g_machine.mapper_regs[0] & 0x08) == 0x00; + if (Addr == 0xFFFE) { + if (in_menu_mode && ((Value & 0xF0) == 0x40)) { + g_machine.mapper_regs[0] = (g_machine.mapper_regs[0] & 0x1E) | (Value & 0x0E) << 4 | (Value & 0x01); + } else if (in_menu_mode && ((Value & 0xE0) == 0x00)) { + g_machine.mapper_regs[0] = (g_machine.mapper_regs[0] & 0xF0) | ((Value & 0x10) >> 4); + } else { + g_machine.mapper_regs[2] = Value & 0x1F; + } + } + if (Addr == 0xFFFF) { + if (in_menu_mode && ((Value & 0xF0) == 0x40)) { + g_machine.mapper_regs[0] |= 0x08; + in_menu_mode = 0; + } else if ((!in_menu_mode) && ((Value & 0xE0) == 0xE0)) { + // Meka extension: this is used to reliably reset the + // mapper to menu mode when loading save states + g_machine.mapper_regs[0] &= ~0x08; + in_menu_mode = 1; + } + g_machine.mapper_regs[1] = Value & 0x1F; + } + const int effective_mapbase = (g_machine.mapper_regs[0] & 0xF0) | ((g_machine.mapper_regs[0] & 0x01) << 4); + const int fixed_or_reg1 = in_menu_mode ? 1 : (g_machine.mapper_regs[2] & 0x1F); + const int fixed_or_reg0 = in_menu_mode ? 0 : (g_machine.mapper_regs[1] & 0x1F); + Map_8k_ROM(0, (effective_mapbase << 1) & tsms.Pages_Mask_8k); + Map_8k_ROM(1, ((effective_mapbase << 1) | 1) & tsms.Pages_Mask_8k); + Map_8k_ROM(2, ((effective_mapbase | fixed_or_reg1) << 1) & tsms.Pages_Mask_8k); + Map_8k_ROM(3, (((effective_mapbase | fixed_or_reg1) << 1) | 1) & tsms.Pages_Mask_8k); + Map_8k_ROM(4, ((effective_mapbase | fixed_or_reg0) << 1) & tsms.Pages_Mask_8k); + Map_8k_ROM(5, (((effective_mapbase | fixed_or_reg0) << 1) | 1) & tsms.Pages_Mask_8k); + //return; + } + + switch (Addr >> 13) + { + // RAM [0xC000] = [0xE000] ------------------------------------------------ + case 6: Mem_Pages[6][Addr] = Value; return; + case 7: Mem_Pages[7][Addr] = Value; return; + } + + Write_Error (Addr, Value); +} + + // Based on MSX ASCII 8KB mapper? http://bifi.msxnet.org/msxnet/tech/megaroms.html#ascii8 // - This mapper requires 4 registers to save bank switching state. // However, all other mappers so far used only 3 registers, stored as 3 bytes. diff --git a/meka/srcs/mappers.h b/meka/srcs/mappers.h index 57d66a32..85b5d30e 100644 --- a/meka/srcs/mappers.h +++ b/meka/srcs/mappers.h @@ -49,6 +49,7 @@ #define MAPPER_SMS_Korean_MD_FFF0 (24) // Registers at 0xFFF0 and 0xFFFF (Mega Mode Super Game 30 [SMS-MD]) #define MAPPER_SMS_Korean_MD_FFF5 (25) // Registers at 0xFFF5 and 0xFFFF (Jaemiissneun Game Mo-eumjip 42/65 Hap [SMS-MD], Pigu Wang Hap ~ Jaemiiss-neun Game Mo-eumjip [SMS-MD]) #define MAPPER_SMS_Korean_MD_FFFA (26) // Registers at 0xFFFA and 0xFFFF (Game Jiphap 30 Hap [SMS-MD]) +#define MAPPER_GG_Super_12_in_1_FFFE (28) // Registers at 0xFFFE 0xFFFF (Super 5/7/12 in 1 Game Gear [Mortal Kombat]/[Bare Knuckle 2]) #define READ_FUNC(_NAME) u8 _NAME(register u16 Addr) #define WRITE_FUNC(_NAME) void _NAME(register u16 Addr, register u8 Value) @@ -94,6 +95,7 @@ WRITE_FUNC (Write_Mapper_SMS_Korean_0000_xor_FF); WRITE_FUNC (Write_Mapper_SMS_Korean_MD_FFF0); WRITE_FUNC (Write_Mapper_SMS_Korean_MD_FFF5); WRITE_FUNC (Write_Mapper_SMS_Korean_MD_FFFA); +WRITE_FUNC (Write_Mapper_GG_Super_12_in_1_FFFE); //----------------------------------------------------------------------------- void Out_SC3000_SurvivorsMulticarts_DataWrite(u8 v); diff --git a/meka/srcs/saves.cpp b/meka/srcs/saves.cpp index 74b6b060..c7ac119b 100644 --- a/meka/srcs/saves.cpp +++ b/meka/srcs/saves.cpp @@ -141,6 +141,26 @@ void Load_Game_Fixup(void) WrZ80_NoHook(0xFFFF, g_machine.mapper_regs[1]); WrZ80_NoHook(0xFFFE, g_machine.mapper_regs[2]); break; + case MAPPER_GG_Super_12_in_1_FFFE: + if (1) { + const int in_menu_mode = (g_machine.mapper_regs[0] & 0x08) == 0x00; + const int mapbase = g_machine.mapper_regs[0] & 0xF1; + const int reg1 = g_machine.mapper_regs[2] & 0x1F; + const int reg0 = g_machine.mapper_regs[1] & 0x1F; + WrZ80_NoHook(0xFFFE, 0xE0); // Meka extension: mapper reset to menu mode + WrZ80_NoHook(0xFFFE, 0x40 | ((mapbase & 0xE0) >> 4) | (mapbase & 0x01)); + WrZ80_NoHook(0xFFFE, mapbase & 0x10); + if (in_menu_mode) { + // Meka extension: reliable mapper register restoration in menu mode + WrZ80_NoHook(0xFFFE, 0x80 | reg1); + WrZ80_NoHook(0xFFFF, reg0); + } else { + WrZ80_NoHook(0xFFFF, 0x40); + WrZ80_NoHook(0xFFFE, reg1); + WrZ80_NoHook(0xFFFF, reg0); + } + } + break; } } @@ -335,6 +355,7 @@ int Save_Game_MSV(FILE *f) case MAPPER_SMS_Korean_MD_FFF0: case MAPPER_SMS_Korean_MD_FFF5: case MAPPER_SMS_Korean_MD_FFFA: + case MAPPER_GG_Super_12_in_1_FFFE: default: fwrite (RAM, 0x2000, 1, f); // Do not use g_driver->ram because of g_driver video mode change break; @@ -513,6 +534,7 @@ int Load_Game_MSV(FILE *f) case MAPPER_SMS_Korean_MD_FFF0: case MAPPER_SMS_Korean_MD_FFF5: case MAPPER_SMS_Korean_MD_FFFA: + case MAPPER_GG_Super_12_in_1_FFFE: default: fread (RAM, 0x2000, 1, f); // Do not use g_driver->ram because of g_driver video mode change break;