diff --git a/meka/compat.txt b/meka/compat.txt index ab16dc0f..0d999cd2 100644 --- a/meka/compat.txt +++ b/meka/compat.txt @@ -1405,6 +1405,11 @@ Street Hero [Proto 1] [SMS-GG] Ok Strider Returns Ok Striker Ok + Super 21 in 1 [Pacmania] [Super 53 in 1] *Ok + Super 32 in 1 [Alien 3] [Super 53 in 1] *Ok + Super 53 in 1 [Alien 3] *Ok + Super 53 in 1 [Pacmania] *Ok + Super 68 in 1 [Simpson] *Ok Super Battletank Ok Super Columns Ok Super Columns (JP) Ok @@ -1498,7 +1503,7 @@ Zoop (US) Ok Zoop [Proto] (US) Ok ----------------------------------------------------------------------------- - 517 games tested - 506 are "Ok" - Compatibility rate: 97.63% + 522 games tested - 511 are "Ok" - Compatibility rate: 97.89% ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- diff --git a/meka/meka.nam b/meka/meka.nam index 77c032bf..8397c422 100644 --- a/meka/meka.nam +++ b/meka/meka.nam @@ -1288,6 +1288,11 @@ 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 05539043 7AAAB32E98A513AB Super 21 in 1 [Pacmania] [Super 53 in 1]/EMU_MAPPER=41 +GG 3b87194e 040AB4999C343E97 Super 32 in 1 [Alien 3] [Super 53 in 1]/EMU_MAPPER=41 +GG 55b18291 7EB467C734D95142 Super 53 in 1 [Alien 3]/EMU_MAPPER=41 +GG 83c15a37 F85E1AF5CC7E64ED Super 53 in 1 [Pacmania]/EMU_MAPPER=41 +GG 179f3519 CDAB487A05130DA1 Super 68 in 1 [Simpson]/EMU_MAPPER=41 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 f23e934f..87095ae7 100644 --- a/meka/srcs/machine.cpp +++ b/meka/srcs/machine.cpp @@ -22,6 +22,7 @@ #include "tvtype.h" #include "sound/fmunit.h" #include "sound/psg.h" +#include "app_game.h" //----------------------------------------------------------------------------- // Data @@ -196,6 +197,9 @@ void Machine_Set_Handler_MemRW(void) case MAPPER_SMS_Korean_MSX_32KB_2000: WrZ80 = WrZ80_NoHook = Write_Mapper_SMS_Korean_MSX_32KB_2000; break; + case MAPPER_GG_Super_68_in_1_FFFE_FFFF: + WrZ80 = WrZ80_NoHook = Write_Mapper_GG_Super_68_in_1_FFFE_FFFF; + break; } } @@ -485,6 +489,25 @@ void Machine_Set_Mapping (void) g_machine.mapper_regs[0] = 0; break; + case MAPPER_GG_Super_68_in_1_FFFE_FFFF: + 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 = 4; + for (int i = 0; i != MAPPER_REGS_MAX; i++) + g_machine.mapper_regs[i] = 0; + g_machine.mapper_regs[2] = 1; + drv_set(DRV_GG); + gamebox_resize_all(); + VDP_UpdateLineLimits(); + Video_GameMode_UpdateBounds(); + 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 00bf3386..53578679 100644 --- a/meka/srcs/mappers.cpp +++ b/meka/srcs/mappers.cpp @@ -14,6 +14,9 @@ #include "shared.h" #include "mappers.h" #include "eeprom.h" +#include "vdp.h" +#include "video.h" +#include "app_game.h" //----------------------------------------------------------------------------- // Data @@ -952,6 +955,123 @@ WRITE_FUNC (Write_Mapper_SMS_Korean_MSX_32KB_2000) Write_Error (Addr, Value); } +// Mapper #41 +// Super 68 in 1 [Simpson] +// Super 53 in 1 [Alien 3] +// +// This also works fine for ghost ROM data: +// +// Super 53 in 1 [Pacmania] +// Super 32 in 1 [Alien 3] [Super 53 in 1] +// Super 21 in 1 [Pacmania] [Super 53 in 1] +WRITE_FUNC(Write_Mapper_GG_Super_68_in_1_FFFE_FFFF) +{ + if ((Addr == 0xFFFE) || (Addr == 0xFFFF)) // Configurable segment ----------------------------------------------- + { + if (Addr == 0xFFFF) + { + g_machine.mapper_regs[1] = Value; + if (! (g_machine.mapper_regs[3] & 0x80)) { + // "menu mode" + if (Value & 0x08) { + g_machine.mapper_regs[1] = 0; + g_machine.mapper_regs[2] = 1; + // switch to "Sega mode" + g_machine.mapper_regs[3] |= 0x80; + bool sms_gg_mode = false; + if (g_machine.mapper_regs[3] & 0x08) { + // SMS-GG mode according to heuristic + int game_id_low = g_machine.mapper_regs[0] & 0x3F; + sms_gg_mode = (game_id_low < 0x14) || (game_id_low == 0x18) || (game_id_low == 0x28); + } else if (g_machine.mapper_regs[3] & 0x04) { + // SMS-GG mode according to game_id & 0x40 + sms_gg_mode = !! (g_machine.mapper_regs[0] & 0x40); + } else { + // SMS-GG mode according to game_id & 0x20 + // (game ID limited to six bits in this mode) + sms_gg_mode = !! (g_machine.mapper_regs[0] & 0x20); + } + if (sms_gg_mode) { + drv_set(DRV_SMS); + } else { + drv_set(DRV_GG); + } + gamebox_resize_all(); + VDP_UpdateLineLimits(); + Video_GameMode_UpdateBounds(); + } + } + } + else if (Addr == 0xFFFE) + { + g_machine.mapper_regs[2] = Value; + if (! (g_machine.mapper_regs[3] & 0x80)) { + // "menu mode" + if ((Value & 0x0C) == 0x0C) { + g_machine.mapper_regs[0] &= 0x3F; + g_machine.mapper_regs[0] |= (Value & 0x03) << 6; + + if (! (g_machine.mapper_regs[3] & 0x08)) { + // choose mechanism for SMS-GG mode signalling in non-heuristic mode: + // store "use game_id & 0x40" bit 0x04 in register 3 + g_machine.mapper_regs[3] = 0x04 | (g_machine.mapper_regs[3] & 0xF3); + } + } else if (Value & 0x08) { + g_machine.mapper_regs[0] &= 0xCF; + g_machine.mapper_regs[0] |= (Value & 0x03) << 4; + } else if (Value & 0x04) { + g_machine.mapper_regs[0] &= 0xF3; + g_machine.mapper_regs[0] |= (Value & 0x03) << 2; + } else { + g_machine.mapper_regs[0] &= 0xFC; + g_machine.mapper_regs[0] |= Value & 0x03; + // choose mechanism for SMS-GG mode signalling: + // store "use heuristic" as bit 0x08 in register 3 (and clear bit 0x04) + g_machine.mapper_regs[3] = (g_machine.mapper_regs[0] & 0x08) | (g_machine.mapper_regs[3] & 0xF3); + } + } + } + + unsigned int game_id = g_machine.mapper_regs[0]; + // when ROM is three megabytes it acts like 4MB layout A B C C + // NOTE: tsms.Pages_Count_8k is actually a maximum value -- not a count! + unsigned int game_id_mask = ((game_id & 0x40) && ((tsms.Pages_Count_8k + 1) / 4 == 0x60)) ? 0x5F : 0xFF; + unsigned int base_page_8k = (game_id & game_id_mask) * 4; + + // NOTE: 68-in-1 hardware masks pages with 0x07 for game_id + // less than 0x20 but 53-in-1 doesn't, and a wider mask + // doesn't break anything for 68-in-1 + unsigned int paging_mask = 0x0F; + + if (! (g_machine.mapper_regs[3] & 0x80)) { + // "menu mode" + Map_8k_ROM(0, base_page_8k & tsms.Pages_Mask_8k); + Map_8k_ROM(1, (base_page_8k | 1) & tsms.Pages_Mask_8k); + Map_8k_ROM(2, (base_page_8k | 2) & tsms.Pages_Mask_8k); + Map_8k_ROM(3, (base_page_8k | 3) & tsms.Pages_Mask_8k); + Map_8k_ROM(4, base_page_8k & tsms.Pages_Mask_8k); + Map_8k_ROM(5, (base_page_8k | 1) & tsms.Pages_Mask_8k); + } else { + // "Sega mode" + Map_8k_ROM(0, base_page_8k & tsms.Pages_Mask_8k); + Map_8k_ROM(1, (base_page_8k | 1) & tsms.Pages_Mask_8k); + Map_8k_ROM(2, (base_page_8k | ((g_machine.mapper_regs[2] & paging_mask) * 2)) & tsms.Pages_Mask_8k); + Map_8k_ROM(3, (base_page_8k | ((g_machine.mapper_regs[2] & paging_mask) * 2) | 1) & tsms.Pages_Mask_8k); + Map_8k_ROM(4, (base_page_8k | ((g_machine.mapper_regs[1] & paging_mask) * 2)) & tsms.Pages_Mask_8k); + Map_8k_ROM(5, (base_page_8k | ((g_machine.mapper_regs[1] & paging_mask) * 2) | 1) & tsms.Pages_Mask_8k); + } + } + + 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 3711266f..b0489c9c 100644 --- a/meka/srcs/mappers.h +++ b/meka/srcs/mappers.h @@ -50,6 +50,7 @@ #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_SMS_Korean_MSX_32KB_2000 (27) // Register at 0x2000 (2 Hap in 1 (Moai-ui bomul, David-2)) +#define MAPPER_GG_Super_68_in_1_FFFE_FFFF (41) // Registers at 0xFFFE and 0xFFFF (Super 68 in 1 [Simpson], Super 53 in 1 [Alien 3]) #define READ_FUNC(_NAME) u8 _NAME(register u16 Addr) #define WRITE_FUNC(_NAME) void _NAME(register u16 Addr, register u8 Value) @@ -96,6 +97,7 @@ 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_SMS_Korean_MSX_32KB_2000); +WRITE_FUNC (Write_Mapper_GG_Super_68_in_1_FFFE_FFFF); //----------------------------------------------------------------------------- void Out_SC3000_SurvivorsMulticarts_DataWrite(u8 v); diff --git a/meka/srcs/saves.cpp b/meka/srcs/saves.cpp index 09bb14b8..e44b369e 100644 --- a/meka/srcs/saves.cpp +++ b/meka/srcs/saves.cpp @@ -16,6 +16,8 @@ #include "vmachine.h" #include "sound/fmunit.h" #include "sound/psg.h" +#include "video.h" +#include "app_game.h" //----------------------------------------------------------------------------- // Functions @@ -26,6 +28,7 @@ void Load_Game_Fixup(void) { int i; u8 b; + bool sms_gg_mode_in_mapper = false; // CPU #ifdef MARAT_Z80 @@ -144,13 +147,62 @@ void Load_Game_Fixup(void) case MAPPER_SMS_Korean_MSX_32KB_2000: WrZ80_NoHook(0x2000, g_machine.mapper_regs[0]); break; + case MAPPER_GG_Super_68_in_1_FFFE_FFFF: + if (1) { + unsigned int game_id = g_machine.mapper_regs[0]; + unsigned int slot_8000_page_offset_16k_FFFF = g_machine.mapper_regs[1]; + unsigned int slot_4000_page_offset_16k_FFFE = g_machine.mapper_regs[2]; + unsigned int mapper_mode = g_machine.mapper_regs[3]; + // mapper is initially in GG mode + drv_set(DRV_GG); + g_machine.mapper_regs[0] = 0x00; + g_machine.mapper_regs[1] = 0x00; + g_machine.mapper_regs[2] = 0x00; + g_machine.mapper_regs[3] = 0x00; + if (mapper_mode & 0x08) { + // "heuristic mode" for SMS-GG determination + WrZ80_NoHook(0xFFFE, 0x06); + } + // low two bits of game ID (also locks in "heuristic mode" bit) + WrZ80_NoHook(0xFFFE, game_id & 0x03); + // lower middle two bits of game ID + WrZ80_NoHook(0xFFFE, 0x04 | ((game_id & 0x0C) >> 2)); + // upper middle two bits of game ID + WrZ80_NoHook(0xFFFE, 0x08 | ((game_id & 0x30) >> 4)); + if ((mapper_mode & 0x0C) || (game_id & 0xC0)) { + // high two bits of game ID + // + // ... and also ... + // + // "use game_id & 0x40" for SMS-GG determination + // when not in heuristic mode + WrZ80_NoHook(0xFFFE, 0x0C | ((game_id & 0xC0) >> 6)); + } + if (mapper_mode & 0x80) { + // "Sega mode" + WrZ80_NoHook(0xFFFF, 0x08); + WrZ80_NoHook(0xFFFF, slot_8000_page_offset_16k_FFFF); + WrZ80_NoHook(0xFFFE, slot_4000_page_offset_16k_FFFE); + } + g_machine.mapper_regs[0] = game_id; + g_machine.mapper_regs[1] = slot_8000_page_offset_16k_FFFF; + g_machine.mapper_regs[2] = slot_4000_page_offset_16k_FFFE; + g_machine.mapper_regs[3] = mapper_mode; + gamebox_resize_all(); + VDP_UpdateLineLimits(); + Video_GameMode_UpdateBounds(); + sms_gg_mode_in_mapper = true; + } + break; } } // VDP/Graphic related - tsms.VDP_Video_Change |= VDP_VIDEO_CHANGE_ALL; - VDP_UpdateLineLimits(); - // FALSE!!! // tsms.VDP_Line = 224; + if (!sms_gg_mode_in_mapper) { + tsms.VDP_Video_Change |= VDP_VIDEO_CHANGE_ALL; + VDP_UpdateLineLimits(); + // FALSE!!! // tsms.VDP_Line = 224; + } // Rewrite all VDP registers (we can do that since it has zero side-effect) for (i = 0; i < 16; i ++) @@ -339,6 +391,7 @@ int Save_Game_MSV(FILE *f) case MAPPER_SMS_Korean_MD_FFF5: case MAPPER_SMS_Korean_MD_FFFA: case MAPPER_SMS_Korean_MSX_32KB_2000: + case MAPPER_GG_Super_68_in_1_FFFE_FFFF: default: fwrite (RAM, 0x2000, 1, f); // Do not use g_driver->ram because of g_driver video mode change break; @@ -518,6 +571,7 @@ int Load_Game_MSV(FILE *f) case MAPPER_SMS_Korean_MD_FFF5: case MAPPER_SMS_Korean_MD_FFFA: case MAPPER_SMS_Korean_MSX_32KB_2000: + case MAPPER_GG_Super_68_in_1_FFFE_FFFF: default: fread (RAM, 0x2000, 1, f); // Do not use g_driver->ram because of g_driver video mode change break;