From d9fb50a9c040f84b2e343c0494aa3cb535ac9c9b Mon Sep 17 00:00:00 2001 From: barbudreadmon Date: Mon, 26 Oct 2026 15:52:02 +0100 Subject: [PATCH 01/26] d_neogeo.cpp: remove Double Dragon One (HB, Early Demo) we already support the final(?) version --- src/burn/drv/neogeo/d_neogeo.cpp | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/burn/drv/neogeo/d_neogeo.cpp b/src/burn/drv/neogeo/d_neogeo.cpp index 49af97e11..035a273ad 100644 --- a/src/burn/drv/neogeo/d_neogeo.cpp +++ b/src/burn/drv/neogeo/d_neogeo.cpp @@ -27937,37 +27937,6 @@ struct BurnDriver BurnDrvDoubled1 = { }; -// Double Dragon One (HB, Early Demo) - -static struct BurnRomInfo doubled1edRomDesc[] = { - { "doubledd-p1.bin", 0x0100000, 0x518ac649, 1 | BRF_ESS | BRF_PRG }, // 0 68K code - { "doubledd-p2.bin", 0x0400000, 0x87cf345c, 1 | BRF_ESS | BRF_PRG }, // 1 - - { "doubledd-s1.bin", 0x0020000, 0x9886a683, 2 | BRF_GRA }, // 2 Text layer tiles - - { "doubledd-c1.bin", 0x1000000, 0x905b6f78, 3 | BRF_GRA }, // 3 Sprite data - { "doubledd-c2.bin", 0x1000000, 0xc677a62f, 3 | BRF_GRA }, // 4 - - { "doubledd-m1.bin", 0x0010000, 0xd20925ae, 4 | BRF_ESS | BRF_PRG }, // 5 Z80 code - - { "doubledd-v1.bin", 0x0800000, 0x197b6305, 5 | BRF_SND }, // 6 Sound data - { "doubledd-v2.bin", 0x0800000, 0x8135d5a8, 5 | BRF_SND }, // 7 -}; - -STDROMPICKEXT(doubled1ed, doubled1ed, neogeo) -STD_ROM_FN(doubled1ed) - -struct BurnDriver BurnDrvDoubled1ed = { - "doubled1ed", "doubled1", "neogeo", NULL, "2025", - "Double Dragon One (HB, Early Demo)\0", NULL, "OzzyOuzo", "Neo Geo MVS", - NULL, NULL, NULL, NULL, - BDF_GAME_WORKING | BDF_CLONE | BDF_HOMEBREW, 2, HARDWARE_SNK_NEOGEO, GBF_SCRFIGHT, 0, - NULL, doubled1edRomInfo, doubled1edRomName, NULL, NULL, NULL, NULL, neogeoInputInfo, neogeoDIPInfo, - NeoInit, NeoExit, NeoFrame, NeoRender, NeoScan, &NeoRecalcPalette, - 0x1000, 320, 224, 4, 3 -}; - - // Bad Dudes - Burger Edition (Demo Version) // https://ozzyouzo.itch.io/bad-dudes From 4ad39f608fc69645b9887471bbf8df7e70bc8425 Mon Sep 17 00:00:00 2001 From: barbudreadmon Date: Mon, 26 Oct 2026 15:54:37 +0100 Subject: [PATCH 02/26] d_neogeo.cpp: fix wjammeb description --- src/burn/drv/neogeo/d_neogeo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/burn/drv/neogeo/d_neogeo.cpp b/src/burn/drv/neogeo/d_neogeo.cpp index 035a273ad..10f4f3113 100644 --- a/src/burn/drv/neogeo/d_neogeo.cpp +++ b/src/burn/drv/neogeo/d_neogeo.cpp @@ -12745,7 +12745,7 @@ struct BurnDriver BurnDrvWjammers = { }; -// Windjammers / Flying Power Disc (Endless Breeze, Hack, v1.2.0) +// Windjammers / Flying Power Disc - Endless Breeze (Hack, v1.2.0) // https://github.com/Boomchil/neo-geo-romhacks/tree/main/public/patches/wjammers static struct BurnRomInfo wjammebRomDesc[] = { @@ -12771,7 +12771,7 @@ STD_ROM_FN(wjammeb) struct BurnDriver BurnDrvWjammeb = { "wjammeb", "wjammers", "neogeo", NULL, "2025-26", - "Windjammers / Flying Power Disc (Endless Breeze, Hack, v1.2.0)\0", NULL, "Boomchil", "Neo Geo MVS", + "Windjammers / Flying Power Disc - Endless Breeze (Hack, v1.2.0)\0", NULL, "Boomchil", "Neo Geo MVS", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_PREFIX_CARTRIDGE | HARDWARE_SNK_NEOGEO, GBF_SPORTSMISC, 0, NULL, wjammebRomInfo, wjammebRomName, NULL, NULL, NULL, NULL, neogeoInputInfo, neogeoDIPInfo, From bacf06ac4f5d8fd9406f25aa682da7e6dcbb25cc Mon Sep 17 00:00:00 2001 From: barbudreadmon Date: Mon, 26 Oct 2026 16:33:54 +0100 Subject: [PATCH 03/26] d_neogeo.cpp: fix pulstarae description --- src/burn/drv/neogeo/d_neogeo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/burn/drv/neogeo/d_neogeo.cpp b/src/burn/drv/neogeo/d_neogeo.cpp index 10f4f3113..0e41ab80f 100644 --- a/src/burn/drv/neogeo/d_neogeo.cpp +++ b/src/burn/drv/neogeo/d_neogeo.cpp @@ -12562,7 +12562,7 @@ struct BurnDriver BurnDrvPulstar = { }; -// Pulstar Aegis (Hack) +// Pulstar Aegis (Hack, v1.0.0) // https://github.com/Boomchil/neo-geo-romhacks/tree/main/public/patches/pulstar static struct BurnRomInfo pulstaraeRomDesc[] = { { "089-p1ae.p1", 0x100000, 0xdcb02236, 1 | BRF_ESS | BRF_PRG }, // 0 68K code / mask rom TC538200 @@ -12590,7 +12590,7 @@ STD_ROM_FN(pulstarae) struct BurnDriver BurnDrvPulstarae = { "pulstarae", "pulstar", "neogeo", NULL, "2026", - "Pulstar Aegis (Hack)\0", NULL, "Boomchil", "Neo Geo MVS", + "Pulstar Aegis (Hack, v1.0.0)\0", NULL, "Boomchil", "Neo Geo MVS", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_CLONE | BDF_HACK | BDF_HISCORE_SUPPORTED, 2, HARDWARE_PREFIX_CARTRIDGE | HARDWARE_SNK_NEOGEO, GBF_HORSHOOT, 0, NULL, pulstaraeRomInfo, pulstaraeRomName, NULL, NULL, NULL, NULL, neogeoInputInfo, neogeoDIPInfo, From b585670e1bfc67c93e942f3da95b07aa94fa4c02 Mon Sep 17 00:00:00 2001 From: barbudreadmon Date: Mon, 26 Oct 2026 16:38:48 +0100 Subject: [PATCH 04/26] Revert "d_neogeo.cpp: remove Double Dragon One (HB, Early Demo)" This reverts commit d9fb50a9c040f84b2e343c0494aa3cb535ac9c9b. --- src/burn/drv/neogeo/d_neogeo.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/burn/drv/neogeo/d_neogeo.cpp b/src/burn/drv/neogeo/d_neogeo.cpp index 0e41ab80f..8487f6144 100644 --- a/src/burn/drv/neogeo/d_neogeo.cpp +++ b/src/burn/drv/neogeo/d_neogeo.cpp @@ -27937,6 +27937,37 @@ struct BurnDriver BurnDrvDoubled1 = { }; +// Double Dragon One (HB, Early Demo) + +static struct BurnRomInfo doubled1edRomDesc[] = { + { "doubledd-p1.bin", 0x0100000, 0x518ac649, 1 | BRF_ESS | BRF_PRG }, // 0 68K code + { "doubledd-p2.bin", 0x0400000, 0x87cf345c, 1 | BRF_ESS | BRF_PRG }, // 1 + + { "doubledd-s1.bin", 0x0020000, 0x9886a683, 2 | BRF_GRA }, // 2 Text layer tiles + + { "doubledd-c1.bin", 0x1000000, 0x905b6f78, 3 | BRF_GRA }, // 3 Sprite data + { "doubledd-c2.bin", 0x1000000, 0xc677a62f, 3 | BRF_GRA }, // 4 + + { "doubledd-m1.bin", 0x0010000, 0xd20925ae, 4 | BRF_ESS | BRF_PRG }, // 5 Z80 code + + { "doubledd-v1.bin", 0x0800000, 0x197b6305, 5 | BRF_SND }, // 6 Sound data + { "doubledd-v2.bin", 0x0800000, 0x8135d5a8, 5 | BRF_SND }, // 7 +}; + +STDROMPICKEXT(doubled1ed, doubled1ed, neogeo) +STD_ROM_FN(doubled1ed) + +struct BurnDriver BurnDrvDoubled1ed = { + "doubled1ed", "doubled1", "neogeo", NULL, "2025", + "Double Dragon One (HB, Early Demo)\0", NULL, "OzzyOuzo", "Neo Geo MVS", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HOMEBREW, 2, HARDWARE_SNK_NEOGEO, GBF_SCRFIGHT, 0, + NULL, doubled1edRomInfo, doubled1edRomName, NULL, NULL, NULL, NULL, neogeoInputInfo, neogeoDIPInfo, + NeoInit, NeoExit, NeoFrame, NeoRender, NeoScan, &NeoRecalcPalette, + 0x1000, 320, 224, 4, 3 +}; + + // Bad Dudes - Burger Edition (Demo Version) // https://ozzyouzo.itch.io/bad-dudes From ad6e940acccd406b3f64efa2893a563576ef495f Mon Sep 17 00:00:00 2001 From: fprietog Date: Mon, 26 Oct 2026 16:42:41 +0100 Subject: [PATCH 05/26] Reapply "d_neogeo.cpp: remove Double Dragon One (HB, Early Demo)" This reverts commit b585670e1bfc67c93e942f3da95b07aa94fa4c02. --- src/burn/drv/neogeo/d_neogeo.cpp | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/burn/drv/neogeo/d_neogeo.cpp b/src/burn/drv/neogeo/d_neogeo.cpp index 8487f6144..0e41ab80f 100644 --- a/src/burn/drv/neogeo/d_neogeo.cpp +++ b/src/burn/drv/neogeo/d_neogeo.cpp @@ -27937,37 +27937,6 @@ struct BurnDriver BurnDrvDoubled1 = { }; -// Double Dragon One (HB, Early Demo) - -static struct BurnRomInfo doubled1edRomDesc[] = { - { "doubledd-p1.bin", 0x0100000, 0x518ac649, 1 | BRF_ESS | BRF_PRG }, // 0 68K code - { "doubledd-p2.bin", 0x0400000, 0x87cf345c, 1 | BRF_ESS | BRF_PRG }, // 1 - - { "doubledd-s1.bin", 0x0020000, 0x9886a683, 2 | BRF_GRA }, // 2 Text layer tiles - - { "doubledd-c1.bin", 0x1000000, 0x905b6f78, 3 | BRF_GRA }, // 3 Sprite data - { "doubledd-c2.bin", 0x1000000, 0xc677a62f, 3 | BRF_GRA }, // 4 - - { "doubledd-m1.bin", 0x0010000, 0xd20925ae, 4 | BRF_ESS | BRF_PRG }, // 5 Z80 code - - { "doubledd-v1.bin", 0x0800000, 0x197b6305, 5 | BRF_SND }, // 6 Sound data - { "doubledd-v2.bin", 0x0800000, 0x8135d5a8, 5 | BRF_SND }, // 7 -}; - -STDROMPICKEXT(doubled1ed, doubled1ed, neogeo) -STD_ROM_FN(doubled1ed) - -struct BurnDriver BurnDrvDoubled1ed = { - "doubled1ed", "doubled1", "neogeo", NULL, "2025", - "Double Dragon One (HB, Early Demo)\0", NULL, "OzzyOuzo", "Neo Geo MVS", - NULL, NULL, NULL, NULL, - BDF_GAME_WORKING | BDF_CLONE | BDF_HOMEBREW, 2, HARDWARE_SNK_NEOGEO, GBF_SCRFIGHT, 0, - NULL, doubled1edRomInfo, doubled1edRomName, NULL, NULL, NULL, NULL, neogeoInputInfo, neogeoDIPInfo, - NeoInit, NeoExit, NeoFrame, NeoRender, NeoScan, &NeoRecalcPalette, - 0x1000, 320, 224, 4, 3 -}; - - // Bad Dudes - Burger Edition (Demo Version) // https://ozzyouzo.itch.io/bad-dudes From 43ee812c5f66945819df0e00c30b025bf024858c Mon Sep 17 00:00:00 2001 From: fprietog Date: Mon, 26 Oct 2026 16:46:10 +0100 Subject: [PATCH 06/26] Revert "Reapply "d_neogeo.cpp: remove Double Dragon One (HB, Early Demo)"" This reverts commit ad6e940acccd406b3f64efa2893a563576ef495f. --- src/burn/drv/neogeo/d_neogeo.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/burn/drv/neogeo/d_neogeo.cpp b/src/burn/drv/neogeo/d_neogeo.cpp index 0e41ab80f..8487f6144 100644 --- a/src/burn/drv/neogeo/d_neogeo.cpp +++ b/src/burn/drv/neogeo/d_neogeo.cpp @@ -27937,6 +27937,37 @@ struct BurnDriver BurnDrvDoubled1 = { }; +// Double Dragon One (HB, Early Demo) + +static struct BurnRomInfo doubled1edRomDesc[] = { + { "doubledd-p1.bin", 0x0100000, 0x518ac649, 1 | BRF_ESS | BRF_PRG }, // 0 68K code + { "doubledd-p2.bin", 0x0400000, 0x87cf345c, 1 | BRF_ESS | BRF_PRG }, // 1 + + { "doubledd-s1.bin", 0x0020000, 0x9886a683, 2 | BRF_GRA }, // 2 Text layer tiles + + { "doubledd-c1.bin", 0x1000000, 0x905b6f78, 3 | BRF_GRA }, // 3 Sprite data + { "doubledd-c2.bin", 0x1000000, 0xc677a62f, 3 | BRF_GRA }, // 4 + + { "doubledd-m1.bin", 0x0010000, 0xd20925ae, 4 | BRF_ESS | BRF_PRG }, // 5 Z80 code + + { "doubledd-v1.bin", 0x0800000, 0x197b6305, 5 | BRF_SND }, // 6 Sound data + { "doubledd-v2.bin", 0x0800000, 0x8135d5a8, 5 | BRF_SND }, // 7 +}; + +STDROMPICKEXT(doubled1ed, doubled1ed, neogeo) +STD_ROM_FN(doubled1ed) + +struct BurnDriver BurnDrvDoubled1ed = { + "doubled1ed", "doubled1", "neogeo", NULL, "2025", + "Double Dragon One (HB, Early Demo)\0", NULL, "OzzyOuzo", "Neo Geo MVS", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HOMEBREW, 2, HARDWARE_SNK_NEOGEO, GBF_SCRFIGHT, 0, + NULL, doubled1edRomInfo, doubled1edRomName, NULL, NULL, NULL, NULL, neogeoInputInfo, neogeoDIPInfo, + NeoInit, NeoExit, NeoFrame, NeoRender, NeoScan, &NeoRecalcPalette, + 0x1000, 320, 224, 4, 3 +}; + + // Bad Dudes - Burger Edition (Demo Version) // https://ozzyouzo.itch.io/bad-dudes From c3c4cc5a00ceb184729e8a5d3d30e7fdc49f719a Mon Sep 17 00:00:00 2001 From: KlaviculaAG <205328730+KlaviculaAG@users.noreply.github.com> Date: Mon, 26 Oct 2026 15:08:03 -0300 Subject: [PATCH 07/26] adj. in d_cps3.cpp. (#2835) OS - p1pkin (in mamedev) o. commit title: Street Fighter III 2nd Impact: Giant Attack (Asia 970930) [msamiullah] --- src/burn/drv/cps3/d_cps3.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/burn/drv/cps3/d_cps3.cpp b/src/burn/drv/cps3/d_cps3.cpp index 117e66a3d..6a0964d88 100644 --- a/src/burn/drv/cps3/d_cps3.cpp +++ b/src/burn/drv/cps3/d_cps3.cpp @@ -512,6 +512,21 @@ static struct BurnRomInfo sfiii2hRomDesc[] = { STD_ROM_PICK(sfiii2h) STD_ROM_FN(sfiii2h) +// --------------------------------------------------------- +// Street Fighter III 2nd Impact: Giant Attack (Asia 970930) +// --------------------------------------------------------- +static struct BurnRomInfo sfiii2aRomDesc[] = { + + { "sfiii2_asia.29f400.u2", 0x080000, 0x4518d589, BRF_ESS | BRF_BIOS }, // SH-2 Bios + +#if !defined ROM_VERIFY + SFIII2_970930_FLASH +#endif +}; + +STD_ROM_PICK(sfiii2a) +STD_ROM_FN(sfiii2a) + // ---------------------------------------------------------------- // Street Fighter III 2nd Impact: Giant Attack (Asia 970930, NO CD) // ---------------------------------------------------------------- @@ -1718,6 +1733,16 @@ struct BurnDriver BurnDrvSfiii2h = { 384, 224, 4, 3 }; +struct BurnDriver BurnDrvSfiii2a = { + "sfiii2a", "sfiii2", NULL, NULL, "1997", + "Street Fighter III 2nd Impact: Giant Attack (Asia 970930)\0", NULL, "Capcom", "CPS-3", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_16BIT_ONLY | BDF_CLONE | BDF_HISCORE_SUPPORTED, 2, HARDWARE_CAPCOM_CPS3, GBF_VSFIGHT, FBF_SF, + NULL, sfiii2aRomInfo, sfiii2aRomName, NULL, NULL, NULL, NULL, cps3InputInfo, sfiiiwideasiaDIPInfo, + sfiii2Init, cps3Exit, cps3Frame, DrvCps3Draw, cps3Scan, &cps3_palette_change, 0x40000, + 384, 224, 4, 3 +}; + struct BurnDriver BurnDrvSfiii2n = { "sfiii2n", "sfiii2", NULL, NULL, "1997", "Street Fighter III 2nd Impact: Giant Attack (Asia 970930, NO CD)\0", NULL, "Capcom", "CPS-3", From a98c99101f467a51d05d70e55e669b14e89eb15a Mon Sep 17 00:00:00 2001 From: Steve Gordon Date: Mon, 26 Oct 2026 11:19:41 -0700 Subject: [PATCH 08/26] Add CPS2+ support and Vampire Arrange sets (#2828) --- makefile.burn_rules | 2 +- projectfiles/qtcreator/fba_sdl.pro | 1 + .../fba_vs2003_libretro_xbox1.vcproj | 3 + .../fba_vs2010_libretro_360.vcxproj | 1 + .../fba_vs2010_libretro_360.vcxproj.filters | 3 + .../visualstudio-2010/fba_vs2010.vcxproj | 1 + .../fba_vs2010.vcxproj.filters | 3 + .../visualstudio-2015/fbneo_vs2015.vcxproj | 1 + .../fbneo_vs2015.vcxproj.filters | 3 + .../visualstudio-2019/fbneo_vs2019.vcxproj | 1 + .../fbneo_vs2019.vcxproj.filters | 3 + .../visualstudio-2022/fbneo_vs2022.vcxproj | 1 + .../fbneo_vs2022.vcxproj.filters | 3 + .../visualstudio-2026/fbneo_vs2026.vcxproj | 1 + .../fbneo_vs2026.vcxproj.filters | 3 + .../xcode/Emulator.xcodeproj/project.pbxproj | 4 + src/burn/drv/capcom/cps.cpp | 75 +- src/burn/drv/capcom/cps.h | 71 +- src/burn/drv/capcom/cps2_crpt.cpp | 4 +- src/burn/drv/capcom/cps_draw.cpp | 5 +- src/burn/drv/capcom/cps_mem.cpp | 154 +- src/burn/drv/capcom/cps_obj.cpp | 108 +- src/burn/drv/capcom/cps_pal.cpp | 58 +- src/burn/drv/capcom/cps_run.cpp | 29 - src/burn/drv/capcom/cps_rw.cpp | 177 +- src/burn/drv/capcom/cps_scr.cpp | 16 +- src/burn/drv/capcom/cpsrd.cpp | 8 +- src/burn/drv/capcom/d_cps2.cpp | 556 ------ src/burn/drv/capcom/d_cps2_custom.cpp | 1630 +++++++++++++++++ src/burn/drv/capcom/meson.build | 1 + src/burn/drv/capcom/qs.cpp | 22 +- src/burn/drv/capcom/qs_c.cpp | 8 +- src/burn/drv/capcom/qs_z.cpp | 32 +- src/burn/drv/capcom/xmvsf3v3.h | 2 +- 34 files changed, 1907 insertions(+), 1083 deletions(-) create mode 100644 src/burn/drv/capcom/d_cps2_custom.cpp diff --git a/makefile.burn_rules b/makefile.burn_rules index 6798ea7fd..c9883798e 100644 --- a/makefile.burn_rules +++ b/makefile.burn_rules @@ -14,7 +14,7 @@ drvsrc = d_akkaarrh.o d_arcadecl.o d_atarig1.o d_atarisy1.o d_atarisy2.o d_badl \ d_cps1.o d_mitchell.o \ \ - d_cps2.o \ + d_cps2.o d_cps2_custom.o \ \ d_cps3.o \ \ diff --git a/projectfiles/qtcreator/fba_sdl.pro b/projectfiles/qtcreator/fba_sdl.pro index aaad627d1..7e74d7857 100644 --- a/projectfiles/qtcreator/fba_sdl.pro +++ b/projectfiles/qtcreator/fba_sdl.pro @@ -317,6 +317,7 @@ $$DRV_CAPCOM { ../../src/burn/drv/capcom/ctv.cpp \ ../../src/burn/drv/capcom/d_cps1.cpp \ ../../src/burn/drv/capcom/d_cps2.cpp \ + ../../src/burn/drv/capcom/d_cps2_custom.cpp \ ../../src/burn/drv/capcom/fcrash_snd.cpp \ ../../src/burn/drv/capcom/kabuki.cpp \ ../../src/burn/drv/capcom/ps_m.cpp \ diff --git a/projectfiles/visualstudio-2003-libretro-xbox1/fba_vs2003_libretro_xbox1.vcproj b/projectfiles/visualstudio-2003-libretro-xbox1/fba_vs2003_libretro_xbox1.vcproj index 44209de13..cf904b980 100644 --- a/projectfiles/visualstudio-2003-libretro-xbox1/fba_vs2003_libretro_xbox1.vcproj +++ b/projectfiles/visualstudio-2003-libretro-xbox1/fba_vs2003_libretro_xbox1.vcproj @@ -363,6 +363,9 @@ + + diff --git a/projectfiles/visualstudio-2010-libretro-360/fba_vs2010_libretro_360.vcxproj b/projectfiles/visualstudio-2010-libretro-360/fba_vs2010_libretro_360.vcxproj index 9d0a566ea..39fdf1805 100644 --- a/projectfiles/visualstudio-2010-libretro-360/fba_vs2010_libretro_360.vcxproj +++ b/projectfiles/visualstudio-2010-libretro-360/fba_vs2010_libretro_360.vcxproj @@ -71,6 +71,7 @@ + diff --git a/projectfiles/visualstudio-2010-libretro-360/fba_vs2010_libretro_360.vcxproj.filters b/projectfiles/visualstudio-2010-libretro-360/fba_vs2010_libretro_360.vcxproj.filters index 50fffd890..510bad522 100644 --- a/projectfiles/visualstudio-2010-libretro-360/fba_vs2010_libretro_360.vcxproj.filters +++ b/projectfiles/visualstudio-2010-libretro-360/fba_vs2010_libretro_360.vcxproj.filters @@ -206,6 +206,9 @@ Source Files\burn\drv\capcom + + Source Files\burn\drv\capcom + Source Files\burn\drv\capcom diff --git a/projectfiles/visualstudio-2010/fba_vs2010.vcxproj b/projectfiles/visualstudio-2010/fba_vs2010.vcxproj index 830dc20f2..6bf7b00a8 100644 --- a/projectfiles/visualstudio-2010/fba_vs2010.vcxproj +++ b/projectfiles/visualstudio-2010/fba_vs2010.vcxproj @@ -529,6 +529,7 @@ rm %(Filename).exe + diff --git a/projectfiles/visualstudio-2010/fba_vs2010.vcxproj.filters b/projectfiles/visualstudio-2010/fba_vs2010.vcxproj.filters index 1e5306fa5..46065db82 100644 --- a/projectfiles/visualstudio-2010/fba_vs2010.vcxproj.filters +++ b/projectfiles/visualstudio-2010/fba_vs2010.vcxproj.filters @@ -1227,6 +1227,9 @@ Burn\drv\capcom + + Burn\drv\capcom + Burn\drv\capcom diff --git a/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj b/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj index 9322c03eb..1c9ebc3d8 100644 --- a/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj +++ b/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj @@ -477,6 +477,7 @@ del %(Filename).exe + diff --git a/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj.filters b/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj.filters index 89a2e7a42..21e6a76ac 100644 --- a/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj.filters +++ b/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj.filters @@ -247,6 +247,9 @@ burn\drv\capcom + + burn\drv\capcom + burn\drv\capcom diff --git a/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj b/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj index c4593fbba..abda156d3 100644 --- a/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj +++ b/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj @@ -477,6 +477,7 @@ del %(Filename).exe + diff --git a/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj.filters b/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj.filters index 176ac1b6b..ab3f09bd2 100644 --- a/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj.filters +++ b/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj.filters @@ -247,6 +247,9 @@ burn\drv\capcom + + burn\drv\capcom + burn\drv\capcom diff --git a/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj b/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj index d8b35add3..d85079b20 100644 --- a/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj +++ b/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj @@ -477,6 +477,7 @@ del %(Filename).exe + diff --git a/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj.filters b/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj.filters index bd19eebe6..a38c4e99a 100644 --- a/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj.filters +++ b/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj.filters @@ -247,6 +247,9 @@ burn\drv\capcom + + burn\drv\capcom + burn\drv\capcom diff --git a/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj b/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj index 4a07b77f9..3c06912b7 100644 --- a/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj +++ b/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj @@ -161,6 +161,7 @@ + diff --git a/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj.filters b/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj.filters index 00c87e883..89e491206 100644 --- a/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj.filters +++ b/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj.filters @@ -391,6 +391,9 @@ burn\drv\capcom + + burn\drv\capcom + burn\drv\capcom diff --git a/projectfiles/xcode/Emulator.xcodeproj/project.pbxproj b/projectfiles/xcode/Emulator.xcodeproj/project.pbxproj index 88a4d498f..27ad2d3c3 100644 --- a/projectfiles/xcode/Emulator.xcodeproj/project.pbxproj +++ b/projectfiles/xcode/Emulator.xcodeproj/project.pbxproj @@ -614,6 +614,7 @@ FE1B262B23561A770065200C /* d_psikyo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE1B208823561A6C0065200C /* d_psikyo.cpp */; }; FE1B262C23561A770065200C /* cpsrd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE1B208A23561A6C0065200C /* cpsrd.cpp */; }; FE1B262D23561A770065200C /* d_cps2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE1B208B23561A6C0065200C /* d_cps2.cpp */; }; + A427B0010000000000000001 /* d_cps2_custom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A427B0010000000000000002 /* d_cps2_custom.cpp */; }; FE1B262E23561A770065200C /* cpst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE1B208C23561A6C0065200C /* cpst.cpp */; }; FE1B262F23561A770065200C /* qs_z.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE1B208D23561A6C0065200C /* qs_z.cpp */; }; FE1B263023561A770065200C /* d_cps1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE1B208E23561A6C0065200C /* d_cps1.cpp */; }; @@ -2156,6 +2157,7 @@ FE1B208823561A6C0065200C /* d_psikyo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = d_psikyo.cpp; sourceTree = ""; }; FE1B208A23561A6C0065200C /* cpsrd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cpsrd.cpp; sourceTree = ""; }; FE1B208B23561A6C0065200C /* d_cps2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = d_cps2.cpp; sourceTree = ""; }; + A427B0010000000000000002 /* d_cps2_custom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = d_cps2_custom.cpp; sourceTree = ""; }; FE1B208C23561A6C0065200C /* cpst.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cpst.cpp; sourceTree = ""; }; FE1B208D23561A6C0065200C /* qs_z.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = qs_z.cpp; sourceTree = ""; }; FE1B208E23561A6C0065200C /* d_cps1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = d_cps1.cpp; sourceTree = ""; }; @@ -4587,6 +4589,7 @@ children = ( FE1B208A23561A6C0065200C /* cpsrd.cpp */, FE1B208B23561A6C0065200C /* d_cps2.cpp */, + A427B0010000000000000002 /* d_cps2_custom.cpp */, FE1B208C23561A6C0065200C /* cpst.cpp */, FE1B208D23561A6C0065200C /* qs_z.cpp */, FE1B208E23561A6C0065200C /* d_cps1.cpp */, @@ -5845,6 +5848,7 @@ FE1B252523561A760065200C /* d_pkscram.cpp in Sources */, FE1B276423561A790065200C /* tilemap_generic.cpp in Sources */, FE1B262D23561A770065200C /* d_cps2.cpp in Sources */, + A427B0010000000000000001 /* d_cps2_custom.cpp in Sources */, FE1B26E923561A780065200C /* d_alpha68k1.cpp in Sources */, FE1B244E23561A750065200C /* 7zBuf.c in Sources */, FE1B23DC23561A750065200C /* m6809_intf.cpp in Sources */, diff --git a/src/burn/drv/capcom/cps.cpp b/src/burn/drv/capcom/cps.cpp index 4e7643165..ebf5992ea 100644 --- a/src/burn/drv/capcom/cps.cpp +++ b/src/burn/drv/capcom/cps.cpp @@ -15,6 +15,8 @@ UINT8 *CpsRom =NULL; UINT32 nCpsRomLen =0; // Program Rom (as in rom) UINT8 *CpsCode=NULL; UINT32 nCpsCodeLen=0; // Program Rom (decrypted) UINT8 *CpsZRom=NULL; UINT32 nCpsZRomLen=0; // Z80 Roms INT8 *CpsQSam=NULL; UINT32 nCpsQSamLen=0; // QSound Sample Roms +UINT32 nCpsQSamMask = 0; +UINT32 nCpsRomMapLen = 0; UINT8 *CpsAd =NULL; UINT32 nCpsAdLen =0; // ADPCM Data UINT8 *CpsStar=NULL; UINT8 *CpsKey=NULL; UINT32 nCpsKeyLen = 0; @@ -1778,23 +1780,7 @@ INT32 Cps2LoadTiles(UINT8* Tile, INT32 nStart) return 0; } -#if 0 -"usual cps2": -rom0 12 -rom1 34 -rom2 56 -rom3 78 - -"interleaved (zero800/cps2turbo)" -rom 12345678 - - 12 12 12 12 - 34 34 34 34 - 56 56 56 56 - 78 78 78 78 -(visualization helper) -#endif - +// Four interleaved ROM lanes in one source image. #define BLOCK_MOD 4 inline static void Cps2LoadBlockInterleaved(UINT8* Tile, UINT8* Sect, INT32 nShift) @@ -1856,19 +1842,6 @@ INT32 Cps2LoadTilesInterleaved(UINT8 *Tile, UINT8 *pSrc, UINT32 nSize) #undef BLOCK_MOD -INT32 Cps2LoadTilesTurbo(UINT8* Tile, INT32 nSize) // cps2turbo -{ - UINT8 *Rom = (UINT8*)BurnMalloc(nSize); - memcpy(Rom, Tile, nSize); - memset(Tile, 0, nSize); - - Cps2LoadTilesInterleaved(Tile, Rom, nSize); - - BurnFree(Rom); - - return 0; -} - INT32 Cps2LoadTilesSplit4(UINT8* Tile, INT32 nStart) { // left side of 16x16 tiles @@ -1986,7 +1959,7 @@ INT32 Cps2LoadTilesGigaman2(UINT8 *Tile, UINT8 *pSrc) static UINT32 nGfxMaxSize; -static INT32 CpsGetROMs(bool bLoad) +static INT32 CpsGetROMs(bool bLoad, INT32 bLinear) { struct BurnRomInfo ri; @@ -2023,7 +1996,7 @@ static INT32 CpsGetROMs(bool bLoad) if ((ri.nType & 0x0f) == CPS2_PRG_68K) { if (bLoad) { if (BurnLoadRom(CpsRomLoad, i, 1)) return 1; - if (Cps2Turbo) BurnByteswap(CpsRomLoad, ri.nLen); + if (bLinear) BurnByteswap(CpsRomLoad, ri.nLen); CpsRomLoad += ri.nLen; } else { nCpsRomLen += ri.nLen; @@ -2052,7 +2025,7 @@ static INT32 CpsGetROMs(bool bLoad) if ((ri.nType & 0x0f) == CPS2_GFX) { if (bLoad) { - if (Cps2Turbo) { + if (bLinear) { BurnLoadRom(CpsGfxLoad, i, 1); CpsGfxLoad += ri.nLen; @@ -2142,7 +2115,7 @@ static INT32 CpsGetROMs(bool bLoad) if ((ri.nType & 0x0f) == CPS2_QSND) { if (bLoad) { BurnLoadRom(CpsQSamLoad, i, 1); - if (Cps2Turbo == 0) { + if (!bLinear) { BurnByteswap(CpsQSamLoad, ri.nLen); } CpsQSamLoad += ri.nLen; @@ -2189,11 +2162,8 @@ static INT32 CpsGetROMs(bool bLoad) ((UINT32*)CpsCode)[i] ^= ((UINT32*)CpsRom)[i]; } #endif - if (Cps2Turbo) { - Cps2LoadTilesTurbo(CpsGfx, nCpsGfxLen); - } - cps2_decrypt_game_data(); + if (nCpsCodeLen && CpsCode == NULL) return 1; // if (!nCpsCodeLen) return 1; } else { @@ -2252,9 +2222,6 @@ INT32 CpsInit() nCPS68KClockspeed = 10000000; } } - if (Cps2Turbo) { - nCPS68KClockspeed = 32000000; - } nCPS68KClockspeed = (INT64)nCPS68KClockspeed * 100 / nBurnFPS; nMemLen = nCpsGfxLen + nCpsRomLen + nCpsCodeLen + nCpsZRomLen + nCpsQSamLen + nCpsAdLen + nCpsKeyLen; @@ -2318,15 +2285,24 @@ INT32 CpsInit() INT32 Cps2Init() { - Cps = 2; + return Cps2Init(0, 0, 0); +} - if (CpsGetROMs(false)) { +INT32 Cps2Init(INT32 nMapLen, UINT32 nGfxSize, INT32 bLinear) +{ + Cps = 2; + nCpsRomMapLen = nMapLen; + + if (CpsGetROMs(false, bLinear)) { return 1; } - CpsInit(); + if (nGfxSize) nCpsGfxLen = nGfxSize; + if (CpsInit()) return 1; - if (CpsGetROMs(true)) { + if (CpsGetROMs(true, bLinear)) { + // Before decryption, CpsCode aliases the shared ROM allocation. + if (!nCpsCodeLen) CpsCode = NULL; return 1; } @@ -2351,6 +2327,7 @@ INT32 CpsExit() nCpsCodeLen = nCpsRomLen = nCpsGfxLen = nCpsZRomLen = nCpsQSamLen = nCpsAdLen = nCpsKeyLen = 0; CpsRom = CpsZRom = CpsAd = CpsStar = NULL; CpsQSam = NULL; + nCpsQSamMask = 0; CpsKey = NULL; // All Memory is allocated to this (this is the only one we can free) @@ -2369,10 +2346,10 @@ INT32 CpsExit() nCpsGlobalXOffset = 0; nCpsGlobalYOffset = 0; - if (Cps2Turbo) { - BurnSampleExit(); - Cps2Turbo = 0; - } + nCpsRomMapLen = 0; + CpsDrawPaletteCallbackFunction = NULL; + Cps2ScrollCallbackFunction = NULL; + Cps2Scroll1CallbackFunction = NULL; return 0; } diff --git a/src/burn/drv/capcom/cps.h b/src/burn/drv/capcom/cps.h index 422661468..c580f7409 100644 --- a/src/burn/drv/capcom/cps.h +++ b/src/burn/drv/capcom/cps.h @@ -28,9 +28,11 @@ extern INT32 nCpsCycles; // Cycles per frame extern INT32 nCpsZ80Cycles; extern UINT8 *CpsGfx; extern UINT32 nCpsGfxLen; // All the graphics extern UINT8 *CpsRom; extern UINT32 nCpsRomLen; // Program Rom (as in rom) +extern UINT32 nCpsRomMapLen; // Optional program mapping size; 0 uses the ROM size extern UINT8 *CpsCode; extern UINT32 nCpsCodeLen; // Program Rom (decrypted) extern UINT8 *CpsZRom; extern UINT32 nCpsZRomLen; // Z80 Roms extern INT8 *CpsQSam; extern UINT32 nCpsQSamLen; // QSound Sample Roms +extern UINT32 nCpsQSamMask; // Optional sample decoder mask; 0 uses the ROM size extern UINT8 *CpsAd; extern UINT32 nCpsAdLen; // ADPCM Data extern UINT8 *CpsKey; extern UINT32 nCpsKeyLen; extern UINT32 nCpsGfxScroll[4]; // Offset to Scroll tiles @@ -38,6 +40,7 @@ extern UINT32 nCpsGfxMask; // Address mask extern UINT8* CpsStar; INT32 CpsInit(); INT32 Cps2Init(); +INT32 Cps2Init(INT32 nMapLen, UINT32 nGfxSize, INT32 bLinear); INT32 CpsExit(); INT32 CpsLoadTiles(UINT8 *Tile,INT32 nStart); INT32 CpsLoadTilesByte(UINT8 *Tile,INT32 nStart); @@ -82,6 +85,7 @@ INT32 CpsLoadStarsByte(UINT8 *pStar, INT32 nStart); INT32 CpsLoadStarsForgottnAlt(UINT8 *pStar, INT32 nStart); INT32 Cps2LoadTiles(UINT8 *Tile,INT32 nStart); INT32 Cps2LoadTilesSIM(UINT8 *Tile,INT32 nStart); +INT32 Cps2LoadTilesInterleaved(UINT8 *Tile, UINT8 *pSrc, UINT32 nSize); INT32 Cps2LoadTilesGigaman2(UINT8 *Tile, UINT8 *pSrc); // cps_config.h @@ -188,6 +192,9 @@ extern INT32 bCpsUpdatePalEveryFrame; INT32 CpsPalInit(); INT32 CpsPalExit(); INT32 CpsPalUpdate(UINT8 *pNewPal); +typedef void (*CpsPalUpdateCallback)(INT32 nPage, const UINT16* pPalette); +extern CpsPalUpdateCallback CpsPalUpdateCallbackFunction; +void CpsPalRecalc(const UINT16* pRaw); // cps_mem.cpp extern UINT8 *CpsRam90; @@ -204,6 +211,13 @@ INT32 CpsAreaScan(INT32 nAction,INT32 *pnMin); typedef INT32 (*CpsMemScanCallback)(INT32, INT32*); extern CpsMemScanCallback CpsMemScanCallbackFunction; +// Additional device state precedes sound state in the scan order. +extern CpsMemScanCallback CpsMemScanExtraCallbackFunction; +// Optional replacement for the QSound state scan. +typedef void (*CpsSoundScanCallback)(INT32, INT32*); +extern CpsSoundScanCallback CpsSoundScanCallbackFunction; + +extern INT32 bCpsObjectBankHandlers; // cps_run.cpp extern UINT8 CpsReset; @@ -287,7 +301,6 @@ extern INT32 Ssf2tb; extern INT32 Dinohunt; extern INT32 Port6SoundWrite; extern INT32 CpsBootlegEEPROM; -extern INT32 Cps2Turbo; extern INT32 Jurassic99; extern INT32 Dinoh; extern INT32 Wofhfh; @@ -312,8 +325,14 @@ void __fastcall CpsWriteWord(UINT32 a, UINT16 d); typedef void (*CpsRWSoundCommandCallback)(UINT16); extern CpsRWSoundCommandCallback CpsRWSoundCommandCallbackFunction; +// Prepare inputs before port compilation; return the number of stock-port players. +typedef INT32 (*CpsRwGetInpCallback)(); +extern CpsRwGetInpCallback CpsRwGetInpCallbackFunction; // cps_draw.cpp +typedef INT32 (*CpsDrawPaletteCallback)(); +// Return nonzero when the palette is already current for this draw. +extern CpsDrawPaletteCallback CpsDrawPaletteCallbackFunction; extern UINT8 CpsRecalcPal; // Flag - If it is 1, recalc the whole palette extern INT32 nCpsLcReg; // Address of layer controller register extern INT32 CpsLayEn[6]; // bits for layer enable @@ -349,9 +368,15 @@ void QsndReset(); void QsndNewFrame(); void QsndEndFrame(); void QsndSyncZ80(); -INT32 QsndScan(INT32 nAction); +INT32 QsndScan(INT32 nAction, INT32 bScanHistory = 0); // qs_z.cpp +extern INT32 nQsndZ80Clock, nQsndZBankOffset; +void QsndZReset(); +void QsndZSetBank(INT32 nBank); +INT32 QsndZGetBank(); +UINT8 __fastcall QsndZRead(UINT16 a); +void __fastcall QsndZWrite(UINT16 a, UINT8 d); INT32 QsndZInit(); INT32 QsndZExit(); INT32 QsndZScan(INT32 nAction); @@ -361,7 +386,7 @@ INT32 QscInit(INT32 nRate); void QscSetRoute(INT32 nIndex, double nVolume, INT32 nRouteDir); void QscReset(); void QscExit(); -INT32 QscScan(INT32 nAction); +INT32 QscScan(INT32 nAction, INT32 bScanHistory = 0); void QscNewFrame(); void QscWrite(INT32 a, INT32 d); UINT8 QscRead(); @@ -411,6 +436,7 @@ INT32 CtvReady(); // cps_obj.cpp extern INT32 nCpsObjectBank; +extern INT32 nCpsObjExtraLen; // Optional extra data bytes per buffered sprite frame extern UINT8 *CpsBootlegSpriteRam; extern INT32 Cps1LockSpriteList910000; extern INT32 Cps1DetectEndSpriteList8000; @@ -419,7 +445,17 @@ typedef INT32 (*Cps1ObjGetCallback)(); extern Cps1ObjGetCallback Cps1ObjGetCallbackFunction; typedef INT32 (*Cps1ObjDrawCallback)(INT32, INT32); extern Cps1ObjDrawCallback Cps1ObjDrawCallbackFunction; +extern INT32 bCpsUseParentBlend; +typedef void (*Cps2ObjGetCallback)(UINT8* pExtra, INT32 nEntry, INT32 nSource); +extern Cps2ObjGetCallback Cps2ObjGetCallbackFunction; +typedef INT32 (*Cps2ObjTileCallback)(UINT16* pObject, INT32 nTile, const UINT8* pExtra, INT32 nEntry); +extern Cps2ObjTileCallback Cps2ObjTileCallbackFunction; +// Negative skips the object, zero uses its palette, positive supplies 16 colors. +typedef INT32 (*Cps2ObjPaletteCallback)(INT32, UINT16, UINT16, INT32, UINT32*); +extern Cps2ObjPaletteCallback Cps2ObjPaletteCallbackFunction; +void CpsObjReset(); +INT32 CpsObjScan(INT32 nAction, char* szName); INT32 CpsObjInit(); INT32 CpsObjExit(); INT32 CpsObjGet(); @@ -449,6 +485,18 @@ INT32 Cps1Scr3Draw(UINT8 *Base,INT32 sx,INT32 sy); INT32 Cps2Scr1Draw(UINT8 *Base,INT32 sx,INT32 sy); INT32 Cps2Scr3Draw(UINT8 *Base,INT32 sx,INT32 sy); +typedef INT32 (*Cps2ScrollCallback)(INT32, INT32, INT32); +extern Cps2ScrollCallback Cps2ScrollCallbackFunction; +typedef const UINT8* (*Cps2Scroll1Callback)(); +extern Cps2Scroll1Callback Cps2Scroll1CallbackFunction; + +// Resolve a scroll tile address, including its layer's graphics offset. +inline static INT32 Cps2ScrollAddress(INT32 nAddress, INT32 nAttributes, INT32 nLayer) +{ + if (Cps2ScrollCallbackFunction) return Cps2ScrollCallbackFunction(nAddress, nAttributes, nLayer); + return nAddress + nCpsGfxScroll[nLayer]; +} + // cpsr.cpp extern UINT8 *CpsrBase; // Tile data base extern INT32 nCpsrScrX,nCpsrScrY; // Basic scroll info @@ -560,21 +608,4 @@ extern UINT16 Cps2VolumeStates[40]; extern INT32 Cps2DisableDigitalVolume; extern UINT8 Cps2VolUp; extern UINT8 Cps2VolDwn; -extern UINT8 AspectDIP; - -// cps_rw.cpp: X-Men Vs. Street Fighter 3v3 + DHC board additions (xmvsf3v3.h) -extern UINT8 CpsXmvSixButtons[6][13]; -extern INT32 CpsXmvSixEnabled; -extern INT32 CpsXmvWide; -INT32 CpsXmvSixInit(INT32 bWide); -void CpsXmvSixExit(); -INT32 CpsXmvSixReset(); -INT32 CpsXmvSixScan(INT32 nAction, INT32* pnMin); -const UINT8* CpsXmvScroll1Extension(); -// ... and its exact save states (run-ahead, netplay): the display state other games rebuild after a load -INT32 CpsXmvVisualScan(INT32 nAction); // cps_mem.cpp -INT32 CpsObjVisualScan(INT32 nAction); // cps_obj.cpp -INT32 CpsPalVisualScan(INT32 nAction); // cps_pal.cpp -INT32 CpsPalConsumeRestoredCache(); - #endif diff --git a/src/burn/drv/capcom/cps2_crpt.cpp b/src/burn/drv/capcom/cps2_crpt.cpp index 81d03e16a..9d9c5a4d7 100644 --- a/src/burn/drv/capcom/cps2_crpt.cpp +++ b/src/burn/drv/capcom/cps2_crpt.cpp @@ -643,8 +643,10 @@ static void cps2_decrypt(const UINT32 *master_key, UINT32 lower_limit, UINT32 up UINT16 *rom = (UINT16 *)CpsRom; UINT32 length = (upper_limit > 0) ? (upper_limit * 2) : nCpsRomLen; if (length > nCpsRomLen) length = nCpsRomLen; - CpsCode = (UINT8*)BurnMalloc(length); + if (nCpsRomMapLen && length > nCpsRomMapLen) length = nCpsRomMapLen; nCpsCodeLen = length; + CpsCode = (UINT8*)BurnMalloc(length); + if (CpsCode == NULL) return; UINT16 *dec = (UINT16*)CpsCode; INT32 i; diff --git a/src/burn/drv/capcom/cps_draw.cpp b/src/burn/drv/capcom/cps_draw.cpp index 5983483fd..b1eade86b 100644 --- a/src/burn/drv/capcom/cps_draw.cpp +++ b/src/burn/drv/capcom/cps_draw.cpp @@ -1,6 +1,8 @@ #include "cps.h" // CPS - Draw +CpsDrawPaletteCallback CpsDrawPaletteCallbackFunction = NULL; + UINT8 CpsRecalcPal = 0; // Flag - If it is 1, recalc the whole palette static INT32 LayerCont; @@ -499,8 +501,7 @@ static void DoDraw(INT32 Recalc) { CtvReady(); // Point to correct tile drawing functions - // xmvsf3v3: a state load restores the effective palette itself; rereading palette RAM would lose it - if (!CpsPalConsumeRestoredCache()) { + if (!CpsDrawPaletteCallbackFunction || !CpsDrawPaletteCallbackFunction()) { if (CpsRecalcPal || bCpsUpdatePalEveryFrame) GetPalette(0, 6); if (Recalc || bCpsUpdatePalEveryFrame) CpsPalUpdate(CpsSavePal); // recalc whole palette if needed } diff --git a/src/burn/drv/capcom/cps_mem.cpp b/src/burn/drv/capcom/cps_mem.cpp index 178756962..ed24db6fc 100644 --- a/src/burn/drv/capcom/cps_mem.cpp +++ b/src/burn/drv/capcom/cps_mem.cpp @@ -16,6 +16,9 @@ UINT8 *CpsRam660=NULL,*CpsRam708=NULL,*CpsReg=NULL,*CpsFrg=NULL; UINT8 *CpsRamFF=NULL; CpsMemScanCallback CpsMemScanCallbackFunction = NULL; +CpsMemScanCallback CpsMemScanExtraCallbackFunction = NULL; +CpsSoundScanCallback CpsSoundScanCallbackFunction = NULL; +INT32 bCpsObjectBankHandlers = 0; // This routine is called first to determine how much memory is needed // and then to set up all the pointers. @@ -87,6 +90,8 @@ void CpsDoMapObjectBanks(INT32 nBank) { nCpsObjectBank = nBank; + if (bCpsObjectBankHandlers) return; + if (nCpsObjectBank) { SekMapMemory(CpsRam708 + 0x8000, 0x708000, 0x709FFF, MAP_RAM); SekMapMemory(CpsRam708 + 0x8000, 0x70A000, 0x70BFFF, MAP_RAM); @@ -117,106 +122,6 @@ INT32 __fastcall CPSResetCallback() return 0; } -// samples: -// empty + 0x3f L, channel 0, sample + 0x00 -// empty + 0x3f R, channel 1, sample + 0x40 - -static void samples_l_start(int channel, int sample, int loop) -{ - BurnSampleChannelPlay(channel + 0x00, sample + 0x00, loop); -} - -static int samples_l_playing() -{ - return BurnSampleGetChannelStatus(0x00) == SAMPLE_PLAYING; -} -static int samples_r_playing() -{ - return BurnSampleGetChannelStatus(0x01) == SAMPLE_PLAYING; -} - -static void samples_r_start(int channel, int sample, int loop) -{ - BurnSampleChannelPlay(channel + 0x01, sample + 0x40, loop); -} - -// ---------------------------------------------------------------------------- -static void qsound_cps2turbo_write(UINT32 offset, UINT8 data) //Zero800 allows you to write and signal when a new song is played -{ - UINT8 *m_qsound_sharedram1 = CpsZRamC0; - UINT8 *m_qsound_sharedram2 = CpsZRamF0; - if (offset == 0x0001 && (data & 0xff) == 0x00 && m_qsound_sharedram1[0x00] == 0xff) //stop samples sound - { - samples_l_start(0, 0, 0); - samples_r_start(0, 0, 0); - bprintf(0, _T("Stop: %02x to %x\n"), offset, offset); - } - - if (offset == 0xffe) - { - if (m_qsound_sharedram1[0x80] == 0x77) //stop music condition in audiocpu = C080 (z80 hacked) - { - samples_l_start(0, 0, 0); //init L samples - samples_r_start(0, 0, 0); //init R samples - m_qsound_sharedram2[0x27] = 0xc9; //Enable Qsound Volume - m_qsound_sharedram1[0x80] = 0xff; //Reset music id flag - m_qsound_sharedram1[0x81] = 0xff; //Reset music id flag - bprintf(0, _T("Clear: %02x to %x\n"), data, offset); - } - else - if (m_qsound_sharedram1[0x81] > 0x00 && m_qsound_sharedram1[0x81] < 0x40) //play music condition in audiocpu = C081 (z80 hacked) - { - int data_byte = m_qsound_sharedram1[0x81]; - m_qsound_sharedram1[0x80] = 0xff; //Reset music id flag - m_qsound_sharedram1[0x81] = 0xff; //Reset music id flag - bprintf(0, _T("Music: %02x to %x\n"), data_byte, offset); - samples_l_start(0, data_byte, 1); - if (samples_l_playing() == 1) - { - m_qsound_sharedram2[0x27] = 0x00; //Qsound music volume = 0 - } - else - samples_l_start(0, 0, 0); //init L samples - - samples_r_start(0, data_byte, 1); - if (samples_r_playing() == 1) - { - m_qsound_sharedram2[0x27] = 0x00; //Qsound music volume = 0 - } - else - samples_r_start(0, 0, 0); //init R samples - } - else - if (m_qsound_sharedram2[0x27] == 0x00 && samples_l_playing() != 1) //if loadstate and qsound volume = 0 && samples stopped - { - samples_l_start(0, 0, 0); //init L samples - samples_r_start(0, 0, 0); //init R samples - m_qsound_sharedram2[0x27] = 0xc9; //Enable Qsound Volume - m_qsound_sharedram1[0x80] = 0xff; //Reset music id flag - m_qsound_sharedram1[0x81] = 0xff; //Reset music id flag - bprintf(0, _T("Volume0: %02x to %x\n"), data, offset); - } - else - if (m_qsound_sharedram2[0x27] != 0x00 && samples_l_playing() == 1) //if loadstate and qsound volume != 0 && samples playing - { - samples_l_start(0, 0, 0); //init L samples - samples_r_start(0, 0, 0); //init R samples - m_qsound_sharedram2[0x27] = 0xc9; //Enable Qsound Volume - m_qsound_sharedram1[0x80] = 0xff; //Reset music id flag - m_qsound_sharedram1[0x81] = 0xff; //Reset music id flag - bprintf(0, _T("Volume0: %02x to %x\n"), data, offset); - } - else - if ((data & 0xff) == 0x55) //init samples in rom start - { - samples_l_start(0, 0, 0); - samples_r_start(0, 0, 0); - } - } - - m_qsound_sharedram1[offset] = data; //Play data Qsound -} - UINT8 __fastcall CPSQSoundC0ReadByte(UINT32 sekAddress) { // bprintf(PRINT_NORMAL, _T(" QS %06X read\n"), sekAddress); @@ -241,10 +146,6 @@ void __fastcall CPSQSoundC0WriteByte(UINT32 sekAddress, UINT8 byteValue) sekAddress &= 0x1FFF; - if (Cps2Turbo) { - qsound_cps2turbo_write(sekAddress >> 1, byteValue); - } - #if 1 && defined USE_SPEEDHACKS // Sync only when the last byte of the sound command is written if (sekAddress == 0x001F) { @@ -361,25 +262,19 @@ INT32 CpsMemInit() SekSetResetCallback(CPSResetCallback); - if (Cps2Turbo) { - // hmmmmmm - bprintf(0, _T("limit 68k to 0x400000\n")); - nCpsRomLen = 0x400000; - nCpsCodeLen = 0x400000; - } - // Map in memory: // 68000 Rom (as seen as is, through read) - SekMapMemory(CpsRom, 0, nCpsRomLen - 1, MAP_READ); + UINT32 nRomMapLen = nCpsRomMapLen ? nCpsRomMapLen : nCpsRomLen; + SekMapMemory(CpsRom, 0, nRomMapLen - 1, MAP_READ); // 68000 Rom (as seen decrypted, through fetch) if (nCpsCodeLen > 0) { // Decoded part (up to nCpsCodeLen) SekMapMemory(CpsCode, 0, nCpsCodeLen - 1, MAP_FETCH); } - if (nCpsRomLen > nCpsCodeLen) { - // The rest (up to nCpsRomLen) - SekMapMemory(CpsRom + nCpsCodeLen, nCpsCodeLen, nCpsRomLen - 1, MAP_FETCH); + if (nRomMapLen > nCpsCodeLen) { + // The remaining program data is fetched without decryption. + SekMapMemory(CpsRom + nCpsCodeLen, nCpsCodeLen, nRomMapLen - 1, MAP_FETCH); } if (Cps == 2) { @@ -456,6 +351,9 @@ INT32 CpsMemExit() BurnFree(CpsMem); CpsMemScanCallbackFunction = NULL; + CpsMemScanExtraCallbackFunction = NULL; + CpsSoundScanCallbackFunction = NULL; + bCpsObjectBankHandlers = 0; return 0; } @@ -541,11 +439,9 @@ INT32 CpsAreaScan(INT32 nAction, INT32 *pnMin) SCAN_VAR(nCps1LayerOffs); } - CpsRwScan(); // scan cps_rw.cpp stuff + if (CpsMemScanExtraCallbackFunction && CpsMemScanExtraCallbackFunction(nAction, pnMin)) return 1; - if (Cps2Turbo) { - BurnSampleScan(nAction, pnMin); - } + CpsRwScan(); // scan cps_rw.cpp stuff if (nAction & ACB_WRITE) { // Palette could have changed CpsRecalcPal = 1; @@ -559,7 +455,8 @@ INT32 CpsAreaScan(INT32 nAction, INT32 *pnMin) } if (((Cps == 2) && !Cps2DisableQSnd) || Cps1Qs == 1) { // Scan QSound chips - QsndScan(nAction); + if (CpsSoundScanCallbackFunction) CpsSoundScanCallbackFunction(nAction, pnMin); + else QsndScan(nAction); } else { // Scan PSound chips if ((Cps & 1) && !Cps1DisablePSnd) PsndScan(nAction, pnMin); } @@ -570,20 +467,3 @@ INT32 CpsAreaScan(INT32 nAction, INT32 *pnMin) return 0; } - -// xmvsf3v3: the display state other games rebuild after a load (latched palette, registers and -// 'four' registers, sprite lag buffers), so its states restore exactly, a paused redraw included -INT32 CpsXmvVisualScan(INT32 nAction) -{ - if (!CpsXmvSixEnabled || !(nAction & ACB_DRIVER_DATA)) return 0; - - struct BurnArea ba; - memset(&ba, 0, sizeof(ba)); - ba.Data = CpsSavePal; ba.nLen = 0x2000; ba.szName = "XMvsSF 3v3 latched palette"; BurnAcb(&ba); - ba.Data = CpsSaveRegData; ba.nLen = 0x100 * (MAX_RASTER + 1); ba.szName = "XMvsSF 3v3 latched registers"; BurnAcb(&ba); - ba.Data = CpsSaveFrgData; ba.nLen = 0x20; ba.szName = "XMvsSF 3v3 latched frg"; BurnAcb(&ba); - - if (CpsObjVisualScan(nAction)) return 1; - - return CpsPalVisualScan(nAction); -} diff --git a/src/burn/drv/capcom/cps_obj.cpp b/src/burn/drv/capcom/cps_obj.cpp index bf7dd8330..3c20ea6fc 100644 --- a/src/burn/drv/capcom/cps_obj.cpp +++ b/src/burn/drv/capcom/cps_obj.cpp @@ -1,8 +1,8 @@ #include "cps.h" -#include "xmvsf3v3.h" // CPS Objs (sprites) INT32 nCpsObjectBank; +INT32 nCpsObjExtraLen = 0; INT32 Sfa2ObjHack = 0; UINT8 *CpsBootlegSpriteRam = NULL; @@ -12,9 +12,14 @@ INT32 Cps1DetectEndSpriteList8000 = 0; Cps1ObjGetCallback Cps1ObjGetCallbackFunction = NULL; Cps1ObjDrawCallback Cps1ObjDrawCallbackFunction = NULL; +Cps2ObjGetCallback Cps2ObjGetCallbackFunction = NULL; +Cps2ObjTileCallback Cps2ObjTileCallbackFunction = NULL; +Cps2ObjPaletteCallback Cps2ObjPaletteCallbackFunction = NULL; +INT32 bCpsUseParentBlend = 1; // Our copy of the sprite table static UINT8 *ObjMem = NULL; +static INT32 nObjMemLen; static INT32 nMax = 0; static INT32 nGetNext = 0; @@ -28,6 +33,7 @@ static INT32 nZOffset; struct ObjFrame { INT32 nShiftX, nShiftY; UINT8* Obj; + UINT8* Extra; INT32 nCount; }; @@ -35,10 +41,12 @@ static INT32 nFrameCount = 0; static struct ObjFrame of[3]; static UINT8 *blendtable; +static UINT32 nBlendCount; static void CpsBlendInit() { blendtable = NULL; + nBlendCount = 0; TCHAR filename[MAX_PATH]; @@ -46,12 +54,8 @@ static void CpsBlendInit() FILE *fa = _tfopen(filename, _T("rt")); - INT32 is_sfz3mix = strstr("sfz3mix", BurnDrvGetTextA(DRV_NAME)) != NULL; - if (fa == NULL) { - if (is_sfz3mix) { - // for sfz3mix, don't use parent's blend table. sfz3mix uses different - // sprite indexing + if (!bCpsUseParentBlend) { return; } @@ -66,8 +70,15 @@ static void CpsBlendInit() bprintf (PRINT_IMPORTANT, _T("Using sprite blending (.bld) table!\n")); - blendtable = (UINT8*)BurnMalloc(0x40000); // maximum number of sprites - memset (blendtable, 0, 0x40000); // no blend + // One entry per 16x16 tile; retain the native range for mirrored tile addresses. + nBlendCount = (nCpsGfxMask + 1) >> 7; + if (nBlendCount < 0x40000) nBlendCount = 0x40000; + blendtable = (UINT8*)BurnMalloc(nBlendCount); + if (blendtable == NULL) { + fclose(fa); + return; + } + memset(blendtable, 0, nBlendCount); char szLine[64]; @@ -97,7 +108,7 @@ static void CpsBlendInit() } for (k = min; k <= max; k++) { - if (k < 0x40000) + if (k < nBlendCount) blendtable[k] = table[type&3]; } } @@ -118,19 +129,17 @@ INT32 CpsObjInit() nFrameCount = 2; // CPS2 sprites lagged by 1 frame and double buffered // CPS1 sprites lagged by 1 frame - ObjMem = (UINT8*)BurnMalloc((nMax << 3) * nFrameCount); + nObjMemLen = ((nMax << 3) + nCpsObjExtraLen) * nFrameCount; + ObjMem = (UINT8*)BurnMalloc(nObjMemLen); if (ObjMem == NULL) { return 1; } - // xmvsf3v3: every buffer its states hold starts the same way - if (CpsXmvSixEnabled) memset(ObjMem, 0, (nMax << 3) * nFrameCount); - // Set up the frame buffers for (INT32 i = 0; i < nFrameCount; i++) { of[i].Obj = ObjMem + (nMax << 3) * i; + of[i].Extra = nCpsObjExtraLen ? ObjMem + (nMax << 3) * nFrameCount + nCpsObjExtraLen * i : NULL; of[i].nCount = 0; - if (CpsXmvSixEnabled) of[i].nShiftX = of[i].nShiftY = 0; } nGetNext=0; @@ -146,11 +155,18 @@ INT32 CpsObjInit() INT32 CpsObjExit() { + Cps2ObjGetCallbackFunction = NULL; + Cps2ObjTileCallbackFunction = NULL; + Cps2ObjPaletteCallbackFunction = NULL; + bCpsUseParentBlend = 1; + nCpsObjExtraLen = 0; + if (blendtable) BurnFree(blendtable); for (INT32 i = 0; i < nFrameCount; i++) { of[i].Obj = NULL; + of[i].Extra = NULL; of[i].nCount = 0; } @@ -167,6 +183,17 @@ INT32 CpsObjExit() return 0; } +void CpsObjReset() +{ + if (ObjMem == NULL) return; + memset(ObjMem, 0, nObjMemLen); + for (INT32 i = 0; i < nFrameCount; i++) { + of[i].nCount = 0; + of[i].nShiftX = of[i].nShiftY = 0; + } + nGetNext = 0; +} + // Get CPS sprites into Obj INT32 CpsObjGet() { @@ -236,6 +263,7 @@ INT32 CpsObjGet() // Okay - this sprite is active: memcpy(po, pg, 8); // copy it over + if (Cps2ObjGetCallbackFunction) Cps2ObjGetCallbackFunction(pof->Extra, pof->nCount, i); pof->nCount++; po += 8; @@ -431,30 +459,17 @@ INT32 Cps2ObjDraw(INT32 nLevelFrom, INT32 nLevelTo) // y -= CpsSaveFrg[0][0xB]; #endif - if (Cps2Turbo) { - if (BURN_ENDIAN_SWAP_INT16(ps[1]) & 0x1000) ps[1] |= BURN_ENDIAN_SWAP_INT16(0x8000); - n |= (BURN_ENDIAN_SWAP_INT16(ps[1]) & 0xe000) << 3; // high bits of address - } else { - n |= (BURN_ENDIAN_SWAP_INT16(ps[1]) & 0x6000) << 3; // high bits of address - } - + n |= (BURN_ENDIAN_SWAP_INT16(ps[1]) & 0x6000) << 3; + // Find the palette for the tiles on this sprite CpstPal = CpsPal + ((a & 0x1F) << 4); - // xmvsf3v3: the HUD's mini portraits in each fighter's colour, decoded from the program ROM UINT32* pStockPal = CpstPal; - UINT32 nPortraitPal[16]; - UINT32 nPortraitColor = 0; - if (CpsXmvSixEnabled && XmvPortraitObject(n, BURN_ENDIAN_SWAP_INT16(ps[0]), BURN_ENDIAN_SWAP_INT16(ps[1]), a, CpsXmvWide != 0, &nPortraitColor)) { - UINT16 nColors[16]; - if (!XmvPortraitColors(CpsRom, nCpsRomLen, n, nPortraitColor, nColors)) { - continue; // the marker isn't a palette, so don't draw it with one - } - for (INT32 pen = 0; pen < 16; pen++) { - UINT32 c = nColors[pen]; - nPortraitPal[pen] = BurnHighCol(((c >> 8) & 15) * 17, ((c >> 4) & 15) * 17, (c & 15) * 17, 0); - } - CpstPal = nPortraitPal; + UINT32 nObjectPal[16]; + if (Cps2ObjPaletteCallbackFunction) { + INT32 nResult = Cps2ObjPaletteCallbackFunction(n, BURN_ENDIAN_SWAP_INT16(ps[0]), BURN_ENDIAN_SWAP_INT16(ps[1]), a, nObjectPal); + if (nResult < 0) continue; + if (nResult) CpstPal = nObjectPal; } nFlip = (a >> 5) & 3; @@ -494,7 +509,11 @@ INT32 Cps2ObjDraw(INT32 nLevelFrom, INT32 nLevelTo) nCpstY = y + (ey << 4); // nCpstTile = n + (dy << 4) + dx; // normal version - nCpstTile = (n & ~0x0F) + (dy << 4) + ((n + dx) & 0x0F); // pgear fix + nCpstTile = (n & ~0x0F) + (dy << 4) + ((n + dx) & 0x0F); // pgear fix + if (Cps2ObjTileCallbackFunction) { + INT32 nEntry = ((UINT8*)ps - pof->Obj) >> 3; + nCpstTile = Cps2ObjTileCallbackFunction(ps, nCpstTile, pof->Extra, nEntry); + } if (Sfa2ObjHack) { // hack to not render blue squares on high score screen after name entry - they should be hidden by priority from what I can see @@ -503,7 +522,7 @@ INT32 Cps2ObjDraw(INT32 nLevelFrom, INT32 nLevelTo) //if (ps[2] == 0xa410) bprintf(PRINT_NORMAL, _T("%x, %x, %x, %x, %x, %x, %x\n"), nCpstTile, a & 0x1f, nCpstX, nCpstY, v, nLevelFrom, nLevelTo); } - nCpsBlend = (blendtable) ? blendtable[nCpstTile] : 0; + nCpsBlend = (blendtable && nCpstTile < nBlendCount) ? blendtable[nCpstTile] : 0; nCpstTile <<= 7; // Find real tile address pCpstOne(); @@ -511,7 +530,7 @@ INT32 Cps2ObjDraw(INT32 nLevelFrom, INT32 nLevelTo) } } - CpstPal = pStockPal; // never leave it pointing at nPortraitPal + CpstPal = pStockPal; // never leave it pointing at nObjectPal } return 0; @@ -907,19 +926,18 @@ INT32 FcrashObjDraw(INT32 nLevelFrom,INT32 nLevelTo) return 0; } -// xmvsf3v3: the sprite lag buffers (the Obj pointers are rebuilt by CpsObjInit, never saved). -// The Z buffer and its counters aren't saved: only drawing changes them, so they'd make a state -// depend on which frames were drawn (netplay replays and run-ahead's hidden frames aren't), and -// each frame's Z values start above what's already in the buffer -INT32 CpsObjVisualScan(INT32 nAction) +// Save the sprite lag buffers, but rebuild their pointers on initialization. +// The Z buffer and counters are drawing scratch: saving them would make states +// depend on which frames were drawn. Each frame starts above the old Z values. +INT32 CpsObjScan(INT32 nAction, char* szName) { - if (!CpsXmvSixEnabled || !(nAction & ACB_DRIVER_DATA)) return 0; + if (!(nAction & ACB_DRIVER_DATA)) return 0; struct BurnArea ba; memset(&ba, 0, sizeof(ba)); ba.Data = ObjMem; - ba.nLen = (nMax << 3) * nFrameCount; - ba.szName = "XMvsSF 3v3 sprite lag buffers"; + ba.nLen = nObjMemLen; + ba.szName = szName; BurnAcb(&ba); SCAN_VAR(nGetNext); diff --git a/src/burn/drv/capcom/cps_pal.cpp b/src/burn/drv/capcom/cps_pal.cpp index 04e41b51b..442145e4e 100644 --- a/src/burn/drv/capcom/cps_pal.cpp +++ b/src/burn/drv/capcom/cps_pal.cpp @@ -3,8 +3,8 @@ // CPS (palette) -static bool CpsXmvPaletteRestored = false; // xmvsf3v3: CpsPalVisualScan() restored CpsPal static UINT8* CpsPalSrc = NULL; // Copy of current input palette +CpsPalUpdateCallback CpsPalUpdateCallbackFunction = NULL; UINT32* CpsPal = NULL; // Hicolor version of palette INT32 nCpsPalCtrlReg; INT32 bCpsUpdatePalEveryFrame = 0; // Some of the hacks need this as they don't write to CpsReg 0x0a @@ -25,8 +25,6 @@ INT32 CpsPalInit() if (CpsPal == NULL) { return 1; } - if (CpsXmvSixEnabled) memset(CpsPal, 0, nLen); - CpsXmvPaletteRestored = false; return 0; } @@ -35,10 +33,21 @@ INT32 CpsPalExit() { BurnFree(CpsPal); BurnFree(CpsPalSrc); + CpsPalUpdateCallbackFunction = NULL; return 0; } -// Update CpsPal with the new palette at pNewPal (length 0xc00 bytes) +static inline UINT32 CpsPalColor(UINT16 nRaw) +{ + INT32 Palette = BURN_ENDIAN_SWAP_INT16(nRaw); + INT32 Bright = 0x0f + ((Palette >> 12) << 1); + INT32 r = ((Palette >> 8) & 0x0f) * 0x11 * Bright / 0x2d; + INT32 g = ((Palette >> 4) & 0x0f) * 0x11 * Bright / 0x2d; + INT32 b = ((Palette >> 0) & 0x0f) * 0x11 * Bright / 0x2d; + return BurnHighCol(r, g, b, 0); +} + +// Update CpsPal with the new palette at pNewPal (length 0xc00 words) INT32 CpsPalUpdate(UINT8* pNewPal) { UINT16 *ps, *pn; @@ -47,23 +56,15 @@ INT32 CpsPalUpdate(UINT8* pNewPal) pn = (UINT16*)pNewPal; memcpy(ps, pn, 0xc00 * sizeof(UINT16)); - + INT32 nCtrl = CpsReg[nCpsPalCtrlReg]; UINT16 *PaletteRAM = (UINT16*)CpsPalSrc; - + for (INT32 nPage = 0; nPage < 6; nPage++) { if (BIT(nCtrl, nPage)) { + if (CpsPalUpdateCallbackFunction) CpsPalUpdateCallbackFunction(nPage, PaletteRAM); for (INT32 Offset = 0; Offset < 0x200; ++Offset) { - INT32 Palette = BURN_ENDIAN_SWAP_INT16(*(PaletteRAM++)); - INT32 r, g, b, Bright; - - Bright = 0x0f + ((Palette >> 12) << 1); - - r = ((Palette >> 8) & 0x0f) * 0x11 * Bright / 0x2d; - g = ((Palette >> 4) & 0x0f) * 0x11 * Bright / 0x2d; - b = ((Palette >> 0) & 0x0f) * 0x11 * Bright / 0x2d; - - CpsPal[(0x200 * nPage) + (Offset ^ 15)] = BurnHighCol(r, g, b, 0); + CpsPal[(0x200 * nPage) + (Offset ^ 15)] = CpsPalColor(*PaletteRAM++); } } else { if (PaletteRAM != (UINT16*)CpsPalSrc) { @@ -75,28 +76,7 @@ INT32 CpsPalUpdate(UINT8* pNewPal) return 0; } -// xmvsf3v3: the effective colours: a palette DMA converts only the pages enabled at the time, and -// palette RAM may already hold the next DMA's colours, so rereading it after a load can't rebuild -// them; the pixel format must not change in between. CpsPalSrc isn't saved: CpsPalUpdate() -// overwrites it before reading it -INT32 CpsPalVisualScan(INT32 nAction) +void CpsPalRecalc(const UINT16* pRaw) { - if (!CpsXmvSixEnabled || !(nAction & ACB_DRIVER_DATA)) return 0; - - struct BurnArea ba; - memset(&ba, 0, sizeof(ba)); - ba.Data = CpsPal; ba.nLen = 0xc00 * sizeof(UINT32); ba.szName = "XMvsSF 3v3 effective palette"; BurnAcb(&ba); - - if (nAction & ACB_WRITE) CpsXmvPaletteRestored = true; - - return 0; -} - -INT32 CpsPalConsumeRestoredCache() -{ - if (!CpsXmvSixEnabled || !CpsXmvPaletteRestored) return 0; - - CpsXmvPaletteRestored = false; - - return 1; + for (INT32 i = 0; i < 0xc00; i++) CpsPal[i ^ 15] = CpsPalColor(pRaw[i]); } diff --git a/src/burn/drv/capcom/cps_run.cpp b/src/burn/drv/capcom/cps_run.cpp index ed1ab37e5..1648f0b69 100644 --- a/src/burn/drv/capcom/cps_run.cpp +++ b/src/burn/drv/capcom/cps_run.cpp @@ -6,7 +6,6 @@ INT32 Cps2Volume = 39; INT32 Cps2DisableDigitalVolume = 0; UINT8 Cps2VolUp; UINT8 Cps2VolDwn; -UINT8 AspectDIP; // only for Cps2Turbo == 1 UINT16 Cps2VolumeStates[40] = { 0xf010, 0xf008, 0xf004, 0xf002, 0xf001, 0xe810, 0xe808, 0xe804, 0xe802, 0xe801, @@ -36,8 +35,6 @@ INT32 Cps1VBlankIRQLine = 2; INT32 Cps1DrawAtVblank = 0; -static UINT8 AspectDIPLast = 0; - CpsRunInitCallback CpsRunInitCallbackFunction = NULL; CpsRunInitCallback CpsRunExitCallbackFunction = NULL; CpsRunResetCallback CpsRunResetCallbackFunction = NULL; @@ -59,23 +56,6 @@ static void CpsQSoundCheatSearchCallback() } } -static void check_aspect() -{ - if (Cps2Turbo && (AspectDIP & 0x03) != AspectDIPLast) { - INT32 aspects[3][2] = { { 16, 9 }, { 4, 3 }, { 112, 81 } }; - AspectDIPLast = AspectDIP & 0x03; - - INT32 nAspectX, nAspectY; - BurnDrvGetAspect(&nAspectX, &nAspectY); - - if (nAspectX != aspects[AspectDIPLast][0] || nAspectY != aspects[AspectDIPLast][1]) { - bprintf(0, _T("* CPS-2: Changing to %d:%d aspect\n"), aspects[AspectDIPLast][0], aspects[AspectDIPLast][1]); - BurnDrvSetAspect(aspects[AspectDIPLast][0], aspects[AspectDIPLast][1]); - Reinitialise(); - } - } -} - static INT32 DrvReset() { // Reset machine @@ -115,11 +95,6 @@ static INT32 DrvReset() CpsRunResetCallbackFunction(); } - if (Cps2Turbo) { - AspectDIPLast = 0xff; - check_aspect(); - } - HiscoreReset(); return 0; @@ -433,10 +408,6 @@ INT32 Cps2Frame() DrvReset(); } - if (Cps2Turbo) { - check_aspect(); - } - // extern INT32 prevline; // prevline = -1; diff --git a/src/burn/drv/capcom/cps_rw.cpp b/src/burn/drv/capcom/cps_rw.cpp index 7c0c12fd8..114056b27 100644 --- a/src/burn/drv/capcom/cps_rw.cpp +++ b/src/burn/drv/capcom/cps_rw.cpp @@ -1,7 +1,8 @@ #include "cps.h" -#include "xmvsf3v3.h" // CPS - Read/Write +CpsRwGetInpCallback CpsRwGetInpCallbackFunction = NULL; + // Input bits #define INP(nnn) UINT8 CpsInp##nnn[8]; CPSINPSET @@ -48,7 +49,6 @@ INT32 Ssf2tb = 0; INT32 Dinohunt = 0; INT32 Port6SoundWrite = 0; INT32 CpsBootlegEEPROM = 0; -INT32 Cps2Turbo = 0; INT32 Jurassic99 = 0; INT32 Dinoh = 0; INT32 Wofhfh = 0; @@ -466,130 +466,8 @@ void CpsWritePort(const UINT32 ia, UINT8 d) // bprintf(PRINT_NORMAL, _T("Write Port %x, %x\n"), ia, d); } -// X-Men Vs. Street Fighter 3v3 + DHC board additions (see xmvsf3v3.h), enabled by its drivers only -UINT8 CpsXmvSixButtons[XMV6_PLAYERS][13]; -INT32 CpsXmvSixEnabled = 0; -INT32 CpsXmvWide = 0; -static UINT16 CpsXmvSixCapabilities = 0; -static XmvSixInputState CpsXmvSixState; -static UINT8 *CpsXmvSixWorkRam = NULL; - -INT32 CpsXmvSixInit(INT32 bWide) -{ - CpsXmvSixWorkRam = (UINT8*)BurnMalloc(XMV6_WORK_RAM_BYTES); - if (CpsXmvSixWorkRam == NULL) { - return 1; - } - - CpsXmvWide = bWide ? 1 : 0; - CpsXmvSixCapabilities = XMV6_CAP_WORK_RAM | XMV6_CAP_PORTRAITS | XMV6_CAP_PORTRAITS_6 | XMV6_CAP_PORTRAITS_12 | XMV6_CAP_PORTRAITS_18; - if (bWide) { - CpsXmvSixCapabilities |= XMV6_CAP_WIDE | XMV6_CAP_WIDE_SCROLL1; - } - CpsXmvSixEnabled = 1; - - memset(CpsXmvSixButtons, 0, sizeof(CpsXmvSixButtons)); - CpsXmvSixReset(); - - return 0; -} - -void CpsXmvSixExit() -{ - CpsXmvSixEnabled = 0; - CpsXmvWide = 0; - BurnFree(CpsXmvSixWorkRam); -} - -INT32 CpsXmvSixReset() -{ - if (!CpsXmvSixEnabled) return 0; - - XmvSixReset(&CpsXmvSixState); - memset(CpsXmvSixWorkRam, 0, XMV6_WORK_RAM_BYTES); - - return 0; -} - -INT32 CpsXmvSixScan(INT32 nAction, INT32* /*pnMin*/) -{ - if (!CpsXmvSixEnabled) return 0; - - if (nAction & ACB_MEMORY_RAM) { - struct BurnArea ba; - memset(&ba, 0, sizeof(ba)); - ba.Data = CpsXmvSixWorkRam; - ba.nLen = XMV6_WORK_RAM_BYTES; - ba.nAddress = XMV6_WORK_RAM_BASE; - ba.szName = "XMvsSF 3v3 work RAM"; - BurnAcb(&ba); - } - - if (nAction & ACB_DRIVER_DATA) { - SCAN_VAR(CpsXmvSixState); - - if (nAction & ACB_WRITE) { - if (CpsXmvSixState.abi != XMV6_VERSION || CpsXmvSixState.count > XMV6_DEPTH) { - // not a state of this board - CpsXmvSixReset(); - return 1; - } - } - } - - return CpsXmvVisualScan(nAction); -} - -// Wide screen: while the ROM keeps 'XS1X' at 0x813ff8, scroll1 columns 64-127 come from 0x814000 -const UINT8* CpsXmvScroll1Extension() -{ - if (!CpsXmvSixEnabled || !CpsXmvWide) return NULL; - - const UINT8* tag = CpsXmvSixWorkRam + 0x3ff8; - if (tag[0] != 'X' || tag[1] != 'S' || tag[2] != '1' || tag[3] != 'X') return NULL; - - return CpsXmvSixWorkRam + 0x4000; -} - -// Once a frame, before the input ports are compiled -static void CpsXmvSixSample() -{ - UINT16 raw[XMV6_PLAYERS]; - for (INT32 p = 0; p < XMV6_PLAYERS; p++) { - raw[p] = 0; - for (INT32 bit = 0; bit < 13; bit++) { - raw[p] |= (CpsXmvSixButtons[p][bit] & 1) << bit; - } - } - XmvSixSample(&CpsXmvSixState, raw); - - // P1 and P2 also reach the stock input ports, for the game's own menus - for (INT32 bit = 0; bit < 7; bit++) { - CpsInp001[bit] = (CpsXmvSixState.held[0] >> bit) & 1; - CpsInp000[bit] = (CpsXmvSixState.held[1] >> bit) & 1; - } - for (INT32 p = 0; p < 2; p++) { - UINT16 value = CpsXmvSixState.held[p]; - CpsInp011[p * 4 + 0] = (value >> 8) & 1; // weak kick - CpsInp011[p * 4 + 1] = (value >> 9) & 1; // medium kick - CpsInp020[p + 0] = (value >> 11) & 1; // start - CpsInp020[p + 4] = (value >> 12) & 1; // coin - } - CpsInp011[2] = (CpsXmvSixState.held[0] >> 10) & 1; // P1 strong kick - CpsInp020[6] = (CpsXmvSixState.held[1] >> 10) & 1; // P2 strong kick -} - UINT8 __fastcall CpsReadByte(UINT32 a) { - if (CpsXmvSixEnabled) { - if (a >= XMV6_BASE && a < XMV6_BASE + XMV6_WINDOW_BYTES) { - return XmvSixReadByte(&CpsXmvSixState, CpsXmvSixCapabilities, a - XMV6_BASE); - } - if (a >= XMV6_WORK_RAM_BASE && a < XMV6_WORK_RAM_BASE + XMV6_WORK_RAM_BYTES) { - return CpsXmvSixWorkRam[a - XMV6_WORK_RAM_BASE]; - } - } - // Input ports mirrored between 0x800000 and 0x807fff if ((a & 0xFF8000) == 0x800000) { return CpsReadPort(a & 0x1FF); @@ -618,11 +496,6 @@ UINT8 __fastcall CpsReadByte(UINT32 a) void __fastcall CpsWriteByte(UINT32 a,UINT8 d) { - if (CpsXmvSixEnabled && a >= XMV6_WORK_RAM_BASE && a < XMV6_WORK_RAM_BASE + XMV6_WORK_RAM_BYTES) { - CpsXmvSixWorkRam[a - XMV6_WORK_RAM_BASE] = d; - return; - } - // Output ports mirrored between 0x800000 and 0x807fff if ((a & 0xFF8000) == 0x800000) { CpsWritePort(a & 0x1FF, d); @@ -631,20 +504,9 @@ void __fastcall CpsWriteByte(UINT32 a,UINT8 d) if (Cps == 2) { // 0x400000 registers - if (Cps2Turbo) { - if ((a & 0xFFFFF0) == 0x665000) { - CpsFrg[a & 0x0F] = d; - return; - } - if ((a & 0xFFFFF0) == 0xfffff0) { - CpsFrg[a & 0x0F] = d; - return; - } - } else { - if ((a & 0xFFFFF0) == 0x400000) { - CpsFrg[a & 0x0F] = d; - return; - } + if ((a & 0xFFFFF0) == 0x400000) { + CpsFrg[a & 0x0F] = d; + return; } if ((a & 0xFF8000) == 0x660000) { if (a == 0x664001) { @@ -671,15 +533,6 @@ void __fastcall CpsWriteByte(UINT32 a,UINT8 d) UINT16 __fastcall CpsReadWord(UINT32 a) { - if (CpsXmvSixEnabled) { - if (a >= XMV6_BASE && a < XMV6_BASE + XMV6_WINDOW_BYTES) { - return XmvSixReadWord(&CpsXmvSixState, CpsXmvSixCapabilities, a - XMV6_BASE); - } - if (a >= XMV6_WORK_RAM_BASE && a < XMV6_WORK_RAM_BASE + XMV6_WORK_RAM_BYTES) { - return XmvSixRamReadWord(CpsXmvSixWorkRam, a - XMV6_WORK_RAM_BASE); - } - } - if ((a & 0xFF8FFF) == 0x800100 + CpsMProt[3]) { return (UINT16)((nCalc[0] * nCalc[1]) >> 16); } @@ -695,11 +548,6 @@ UINT16 __fastcall CpsReadWord(UINT32 a) void __fastcall CpsWriteWord(UINT32 a, UINT16 d) { - if (CpsXmvSixEnabled && a >= XMV6_WORK_RAM_BASE && a < XMV6_WORK_RAM_BASE + XMV6_WORK_RAM_BYTES) { - XmvSixRamWriteWord(CpsXmvSixWorkRam, a - XMV6_WORK_RAM_BASE, d); - return; - } - // ports mirrored between 0x800000 and 0x807fff if ((a & 0xFF8FFF) == 0x800100 + CpsMProt[0]) nCalc[0] = d; @@ -742,6 +590,7 @@ INT32 CpsRwInit() INT32 CpsRwExit() { + CpsRwGetInpCallbackFunction = NULL; InpBlank(); CpsRWSoundCommandCallbackFunction = NULL; return 0; @@ -749,9 +598,7 @@ INT32 CpsRwExit() INT32 CpsRwGetInp() { - if (CpsXmvSixEnabled) { - CpsXmvSixSample(); - } + INT32 nPortPlayers = CpsRwGetInpCallbackFunction ? CpsRwGetInpCallbackFunction() : nMaxPlayers; // Compile separate buttons into Inpxxx #define INP(nnn) \ @@ -862,22 +709,20 @@ INT32 CpsRwGetInp() } } - // xmvsf3v3: players 3-6 never reach the stock ports (Inp011 is P1/P2's kicks there), and - // CpsXmvSixSample() has already cleaned every player's opposite directions - if (nMaxPlayers > 2 && !CpsXmvSixEnabled) { + if (nPortPlayers > 2) { if (Cps == 2) { clear_opposite.check(2, Inp011, 0x08, 0x04, 0x02, 0x01, nSocd[2]); - if (nMaxPlayers == 4) { + if (nPortPlayers == 4) { clear_opposite.check(3, Inp010, 0x08, 0x04, 0x02, 0x01, nSocd[3]); } } else { clear_opposite.check(2, Inp177, 0x08, 0x04, 0x02, 0x01, nSocd[2]); - if (nMaxPlayers == 4) { + if (nPortPlayers == 4) { clear_opposite.check(3, Inp179, 0x08, 0x04, 0x02, 0x01, nSocd[3]); } if (Cps1Qs) { clear_opposite.check(2, Inpc001, 0x08, 0x04, 0x02, 0x01, nSocd[2]); - if (nMaxPlayers == 4) { + if (nPortPlayers == 4) { clear_opposite.check(3, Inpc003, 0x08, 0x04, 0x02, 0x01, nSocd[3]); } } diff --git a/src/burn/drv/capcom/cps_scr.cpp b/src/burn/drv/capcom/cps_scr.cpp index 6e5259204..0c656376d 100644 --- a/src/burn/drv/capcom/cps_scr.cpp +++ b/src/burn/drv/capcom/cps_scr.cpp @@ -1,5 +1,8 @@ #include "cps.h" +Cps2ScrollCallback Cps2ScrollCallbackFunction = NULL; +Cps2Scroll1Callback Cps2Scroll1CallbackFunction = NULL; + // CPS Scroll (Background Layers) // Base = 0x4000 long tile map @@ -82,8 +85,8 @@ INT32 Cps2Scr1Draw(UINT8 *Base, INT32 sx, INT32 sy) INT32 nKnowBlank = -1; // The tile we know is blank INT32 nXTile = nCpsScreenWidth>>3; // 8x8 tiles - // xmvsf3v3 wide screen: scroll1 columns 64-127 can come from the board's extra work RAM - const UINT8* pExt = CpsXmvScroll1Extension(); + // A board may supply the upper half of the scroll1 tilemap. + const UINT8* pExt = Cps2Scroll1CallbackFunction ? Cps2Scroll1CallbackFunction() : NULL; ix = (sx >> 3) + 1; sx &= 7; @@ -115,10 +118,10 @@ INT32 Cps2Scr1Draw(UINT8 *Base, INT32 sx, INT32 sy) t = pe ? ((pe[0] << 8) | pe[1]) : BURN_ENDIAN_SWAP_INT16(pst[0]); t <<= 6; // Get real tile address - t += nCpsGfxScroll[1]; // add on offset to scroll tiles + a = pe ? ((pe[2] << 8) | pe[3]) : BURN_ENDIAN_SWAP_INT16(pst[1]); + t = Cps2ScrollAddress(t, a, 1); if (t != nKnowBlank) { // Draw tile - a = pe ? ((pe[2] << 8) | pe[3]) : BURN_ENDIAN_SWAP_INT16(pst[1]); CpstSetPal(0x20 | (a & 0x1F)); @@ -242,11 +245,10 @@ INT32 Cps2Scr3Draw(UINT8 *Base, INT32 sx, INT32 sy) if(Xmcota && t>=0x5800) t-=0x4000; else if(Ssf2t && t<0x5600) t+=0x4000; t <<= 9; // Get real tile address - if (Cps2Turbo) t &= ~nCpsGfxScroll[3]; // buggy hack - t += nCpsGfxScroll[3]; // add on offset to scroll tiles + a = BURN_ENDIAN_SWAP_INT16(pst[1]); + t = Cps2ScrollAddress(t, a, 3); if (t != nKnowBlank) { // Draw tile - a = BURN_ENDIAN_SWAP_INT16(pst[1]); CpstSetPal(0x60 | (a & 0x1F)); diff --git a/src/burn/drv/capcom/cpsrd.cpp b/src/burn/drv/capcom/cpsrd.cpp index af1fb8032..996722e2c 100644 --- a/src/burn/drv/capcom/cpsrd.cpp +++ b/src/burn/drv/capcom/cpsrd.cpp @@ -76,9 +76,9 @@ static void Cps2TileLine(INT32 y,INT32 sx) pst=FindTile(ix+x,iy+y); t = BURN_ENDIAN_SWAP_INT16(pst[0]); t<<=7; // Get real tile address - t+=nCpsGfxScroll[2]; // add on offset to scroll tiles + a = BURN_ENDIAN_SWAP_INT16(pst[1]); + t = Cps2ScrollAddress(t, a, 2); if (t==nKnowBlank) continue; // Don't draw: we know it's blank - a = BURN_ENDIAN_SWAP_INT16(pst[1]); CpstSetPal(0x40 | (a&0x1f)); nCpstX=sx+(x<<4); nCpstTile=t; nCpstFlip=(a>>5)&3; @@ -185,10 +185,10 @@ static void Cps2TileLineRows(INT32 y,struct CpsrLineInfo *pli) pst=FindTile(tx,iy+y); t = BURN_ENDIAN_SWAP_INT16(pst[0]); t<<=7; // Get real tile address - t+=nCpsGfxScroll[2]; // add on offset to scroll tiles + a = BURN_ENDIAN_SWAP_INT16(pst[1]); + t = Cps2ScrollAddress(t, a, 2); if (t==nKnowBlank) continue; // Don't draw: we know it's blank - a = BURN_ENDIAN_SWAP_INT16(pst[1]); CpstSetPal(0x40 | (a&0x1f)); diff --git a/src/burn/drv/capcom/d_cps2.cpp b/src/burn/drv/capcom/d_cps2.cpp index f3274cbdf..2c1703cc5 100644 --- a/src/burn/drv/capcom/d_cps2.cpp +++ b/src/burn/drv/capcom/d_cps2.cpp @@ -40,57 +40,6 @@ static struct BurnInputInfo Cps2FightingInputList[] = { STDINPUTINFO(Cps2Fighting) -static struct BurnInputInfo Sfz3mixInputList[] = { - {"P1 Coin" , BIT_DIGITAL , CpsInp020+4, "p1 coin" }, - {"P1 Start" , BIT_DIGITAL , CpsInp020+0, "p1 start" }, - {"P1 Up" , BIT_DIGITAL , CpsInp001+3, "p1 up" }, - {"P1 Down" , BIT_DIGITAL , CpsInp001+2, "p1 down" }, - {"P1 Left" , BIT_DIGITAL , CpsInp001+1, "p1 left" }, - {"P1 Right" , BIT_DIGITAL , CpsInp001+0, "p1 right" }, - {"P1 Weak Punch" , BIT_DIGITAL , CpsInp001+4, "p1 fire 1" }, - {"P1 Medium Punch" , BIT_DIGITAL , CpsInp001+5, "p1 fire 2" }, - {"P1 Strong Punch" , BIT_DIGITAL , CpsInp001+6, "p1 fire 3" }, - {"P1 Weak Kick" , BIT_DIGITAL , CpsInp011+0, "p1 fire 4" }, - {"P1 Medium Kick" , BIT_DIGITAL , CpsInp011+1, "p1 fire 5" }, - {"P1 Strong Kick" , BIT_DIGITAL , CpsInp011+2, "p1 fire 6" }, - - {"P2 Coin" , BIT_DIGITAL , CpsInp020+5, "p2 coin" }, - {"P2 Start" , BIT_DIGITAL , CpsInp020+1, "p2 start" }, - {"P2 Up" , BIT_DIGITAL , CpsInp000+3, "p2 up" }, - {"P2 Down" , BIT_DIGITAL , CpsInp000+2, "p2 down" }, - {"P2 Left" , BIT_DIGITAL , CpsInp000+1, "p2 left" }, - {"P2 Right" , BIT_DIGITAL , CpsInp000+0, "p2 right" }, - {"P2 Weak Punch" , BIT_DIGITAL , CpsInp000+4, "p2 fire 1" }, - {"P2 Medium Punch" , BIT_DIGITAL , CpsInp000+5, "p2 fire 2" }, - {"P2 Strong Punch" , BIT_DIGITAL , CpsInp000+6, "p2 fire 3" }, - {"P2 Weak Kick" , BIT_DIGITAL , CpsInp011+4, "p2 fire 4" }, - {"P2 Medium Kick" , BIT_DIGITAL , CpsInp011+5, "p2 fire 5" }, - {"P2 Strong Kick" , BIT_DIGITAL , CpsInp020+6, "p2 fire 6" }, - - {"Reset" , BIT_DIGITAL , &CpsReset , "reset" }, - {"Diagnostic" , BIT_DIGITAL , CpsInp021+1, "diag" }, - {"Service" , BIT_DIGITAL , CpsInp021+2, "service" }, - {"Volume Up" , BIT_DIGITAL , &Cps2VolUp , "volumeup" }, - {"Volume Down" , BIT_DIGITAL , &Cps2VolDwn, "volumedown"}, - {"Dip A" , BIT_DIPSWITCH, &AspectDIP , "dip" }, -}; - -STDINPUTINFO(Sfz3mix) - -// sfz3mix 0.20+ only! -static struct BurnDIPInfo Sfz3mixDIPList[] = -{ - DIP_OFFSET(0x1d) - {0x00, 0xff, 0xff, 0x00, NULL }, - - {0 , 0xfe, 0 , 3, "Aspect Ratio" }, - {0x00, 0x01, 0x03, 0x00, "16:9" }, - {0x00, 0x01, 0x03, 0x01, "4:3" }, - {0x00, 0x01, 0x03, 0x02, "112:81" }, -}; - -STDDIPINFO(Sfz3mix) - static struct BurnInputInfo NineXXInputList[] = { {"P1 Coin" , BIT_DIGITAL , CpsInp020+4, "p1 coin" }, {"P1 Start" , BIT_DIGITAL , CpsInp020+0, "p1 start" }, @@ -9997,7 +9946,6 @@ static INT32 DrvExit() Ssf2tb = 0; Xmcota = 0; Ecofght = 0; - CpsXmvSixExit(); Cps2Volume = 39; Cps2DisableDigitalVolume = 0; @@ -14146,160 +14094,6 @@ static struct BurnRomInfo Sfz3jr2dRomDesc[] = { STD_ROM_PICK(Sfz3jr2d) STD_ROM_FN(Sfz3jr2d) -// Street Fighter Zero 3 Mix v0.32 (hacked by Zero800) -// https://sfz3mix.github.io/ - -static struct BurnSampleInfo Sfz3mixSampleDesc[] = { - { "empty", SAMPLE_NOLOOP }, - { "L01", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L02", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L03", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L04", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L05", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L06", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L07", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L08", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L09", SAMPLE_NOLOOP }, - { "L0A", SAMPLE_NOLOOP }, - { "L0B", SAMPLE_NOLOOP }, - { "L0C", SAMPLE_NOLOOP }, - { "L0D", SAMPLE_NOLOOP }, - { "L0E", SAMPLE_NOLOOP }, - { "L0F", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L10", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L11", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L12", SAMPLE_NOLOOP }, - { "L13", SAMPLE_NOLOOP }, - { "L14", SAMPLE_NOLOOP }, - { "L15", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L16", SAMPLE_NOLOOP }, - { "L17", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L18", SAMPLE_NOLOOP }, - { "L19", SAMPLE_NOLOOP }, - { "L1A", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L1B", SAMPLE_NOLOOP }, - { "L1C", SAMPLE_NOLOOP }, - { "L1D", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L1E", SAMPLE_NOLOOP }, - { "L1F", SAMPLE_NOLOOP }, - { "L20", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L21", SAMPLE_NOLOOP }, - { "L22", SAMPLE_NOLOOP }, - { "L23", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L24", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L25", SAMPLE_NOLOOP }, - { "L26", SAMPLE_NOLOOP }, - { "L27", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L28", SAMPLE_NOLOOP }, - { "L29", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L2A", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L2B", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L2C", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L2D", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L2E", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L2F", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L30", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L31", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L32", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L33", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L34", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L35", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L36", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L37", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L38", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L39", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L3A", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L3B", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L3C", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L3D", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L3E", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "L3F", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "empty", SAMPLE_NOLOOP }, - { "R01", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R02", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R03", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R04", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R05", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R06", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R07", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R08", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R09", SAMPLE_NOLOOP }, - { "R0A", SAMPLE_NOLOOP }, - { "R0B", SAMPLE_NOLOOP }, - { "R0C", SAMPLE_NOLOOP }, - { "R0D", SAMPLE_NOLOOP }, - { "R0E", SAMPLE_NOLOOP }, - { "R0F", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R10", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R11", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R12", SAMPLE_NOLOOP }, - { "R13", SAMPLE_NOLOOP }, - { "R14", SAMPLE_NOLOOP }, - { "R15", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R16", SAMPLE_NOLOOP }, - { "R17", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R18", SAMPLE_NOLOOP }, - { "R19", SAMPLE_NOLOOP }, - { "R1A", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R1B", SAMPLE_NOLOOP }, - { "R1C", SAMPLE_NOLOOP }, - { "R1D", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R1E", SAMPLE_NOLOOP }, - { "R1F", SAMPLE_NOLOOP }, - { "R20", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R21", SAMPLE_NOLOOP }, - { "R22", SAMPLE_NOLOOP }, - { "R23", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R24", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R25", SAMPLE_NOLOOP }, - { "R26", SAMPLE_NOLOOP }, - { "R27", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R28", SAMPLE_NOLOOP }, - { "R29", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R2A", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R2B", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R2C", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R2D", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R2E", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R2F", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R30", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R31", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R32", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R33", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R34", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R35", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R36", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R37", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R38", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R39", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R3A", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R3B", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R3C", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R3D", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R3E", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "R3F", SAMPLE_NOLOOP | SAMPLE_NODUMP }, - { "", 0 } -}; - -STD_SAMPLE_PICK(Sfz3mix) -STD_SAMPLE_FN(Sfz3mix) - -static struct BurnRomInfo Sfz3mixRomDesc[] = { - { "sz3mx.03", 0x0600000, 0x7a968da6, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - - { "sz3mx.13m", 0x2000000, 0x5389b651, CPS2_GFX | BRF_GRA }, - { "sz3mx.21m", 0x2000000, 0x914c7031, CPS2_GFX | BRF_GRA }, - - { "sz3mx.01", 0x0080000, 0x52f98a5c, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - - { "sz3mx.11m", 0x1000000, 0x395edce2, CPS2_QSND | BRF_SND }, - - { "phoenix.key", 0x0000014, 0x2cf772b0, CPS2_ENCRYPTION_KEY }, -}; - -STD_ROM_PICK(Sfz3mix) -STD_ROM_FN(Sfz3mix) - // Street Fighter Zero 3 Mix v0.13 (hacked by Zero800) // https://sfz3mix.github.io/ @@ -15303,37 +15097,6 @@ struct BurnDriver BurnDrvCpsSfz3jr2d = { &CpsRecalcPal, 0x1000, 384, 224, 4, 3 }; -static INT32 Cps2TurboInit() -{ - Cps2Turbo = 1; - - INT32 rc = PhoenixInit(); - - if (!rc) { - BurnSampleInit(1); - for (int i = 0; i < 0x40; i++) { - // Left side - BurnSampleSetRoute(i + 0x00, BURN_SND_SAMPLE_ROUTE_1, 0.10, BURN_SND_ROUTE_LEFT); - BurnSampleSetRoute(i + 0x00, BURN_SND_SAMPLE_ROUTE_2, 0.10, BURN_SND_ROUTE_NONE); - // Right side - BurnSampleSetRoute(i + 0x40, BURN_SND_SAMPLE_ROUTE_1, 0.10, BURN_SND_ROUTE_RIGHT); - BurnSampleSetRoute(i + 0x40, BURN_SND_SAMPLE_ROUTE_2, 0.10, BURN_SND_ROUTE_NONE); - } - } - - return rc; -} - -struct BurnDriver BurnDrvCpsSfz3mix = { - "sfz3mix", "sfa3", NULL, "sfz3mix", "2026", - "Street Fighter Zero 3 Mix v0.32\0", NULL, "hack (zero800)", "CPS2", - NULL, NULL, NULL, NULL, - BDF_GAME_WORKING | BDF_CLONE | BDF_HACK | BDF_HISCORE_SUPPORTED, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_SF, - NULL, Sfz3mixRomInfo, Sfz3mixRomName, NULL, NULL, Sfz3mixSampleInfo, Sfz3mixSampleName, Sfz3mixInputInfo, Sfz3mixDIPInfo, - Cps2TurboInit, DrvExit, Cps2Frame, CpsRedraw, CpsAreaScan, - &CpsRecalcPal, 0x1000, 416, 234, 16, 9 -}; - struct BurnDriver BurnDrvCpsSfz3mix13 = { "sfz3mix13", "sfa3", NULL, NULL, "2022", "Street Fighter Zero 3 Mix v0.13\0", NULL, "hack", "CPS2", @@ -16618,325 +16381,6 @@ struct BurnDriver BurnDrvCpsXmvsfcph = { &CpsRecalcPal, 0x1000, 384, 224, 4, 3 }; -// X-Men Vs. Street Fighter 3v3 + DHC (Hack, v1.1.0) -// Modified by Morphus56K -// 2026-10-11 -// https://www.morphus56k.com/blog/xmvsf-three-on-three -// Three characters a team, Delayed Hyper Combos, snapbacks and eighteen colours per character. -// The 6 player sets put P1-P3 against P4-P6; the wide sets show a 512 pixel wide arena. -// All four need the board additions in xmvsf3v3.h (inputs for six players, extra work RAM, -// HUD portraits in the chosen colour). - -static struct BurnInputInfo Xmvsf3v3InputList[] = { - {"P1 Coin" , BIT_DIGITAL , CpsXmvSixButtons[0] + 12, "p1 coin" }, - {"P1 Start" , BIT_DIGITAL , CpsXmvSixButtons[0] + 11, "p1 start" }, - {"P1 Up" , BIT_DIGITAL , CpsXmvSixButtons[0] + 3 , "p1 up" }, - {"P1 Down" , BIT_DIGITAL , CpsXmvSixButtons[0] + 2 , "p1 down" }, - {"P1 Left" , BIT_DIGITAL , CpsXmvSixButtons[0] + 1 , "p1 left" }, - {"P1 Right" , BIT_DIGITAL , CpsXmvSixButtons[0] + 0 , "p1 right" }, - {"P1 Weak Punch" , BIT_DIGITAL , CpsXmvSixButtons[0] + 4 , "p1 fire 1" }, - {"P1 Medium Punch" , BIT_DIGITAL , CpsXmvSixButtons[0] + 5 , "p1 fire 2" }, - {"P1 Strong Punch" , BIT_DIGITAL , CpsXmvSixButtons[0] + 6 , "p1 fire 3" }, - {"P1 Weak Kick" , BIT_DIGITAL , CpsXmvSixButtons[0] + 8 , "p1 fire 4" }, - {"P1 Medium Kick" , BIT_DIGITAL , CpsXmvSixButtons[0] + 9 , "p1 fire 5" }, - {"P1 Strong Kick" , BIT_DIGITAL , CpsXmvSixButtons[0] + 10, "p1 fire 6" }, - - {"P2 Coin" , BIT_DIGITAL , CpsXmvSixButtons[1] + 12, "p2 coin" }, - {"P2 Start" , BIT_DIGITAL , CpsXmvSixButtons[1] + 11, "p2 start" }, - {"P2 Up" , BIT_DIGITAL , CpsXmvSixButtons[1] + 3 , "p2 up" }, - {"P2 Down" , BIT_DIGITAL , CpsXmvSixButtons[1] + 2 , "p2 down" }, - {"P2 Left" , BIT_DIGITAL , CpsXmvSixButtons[1] + 1 , "p2 left" }, - {"P2 Right" , BIT_DIGITAL , CpsXmvSixButtons[1] + 0 , "p2 right" }, - {"P2 Weak Punch" , BIT_DIGITAL , CpsXmvSixButtons[1] + 4 , "p2 fire 1" }, - {"P2 Medium Punch" , BIT_DIGITAL , CpsXmvSixButtons[1] + 5 , "p2 fire 2" }, - {"P2 Strong Punch" , BIT_DIGITAL , CpsXmvSixButtons[1] + 6 , "p2 fire 3" }, - {"P2 Weak Kick" , BIT_DIGITAL , CpsXmvSixButtons[1] + 8 , "p2 fire 4" }, - {"P2 Medium Kick" , BIT_DIGITAL , CpsXmvSixButtons[1] + 9 , "p2 fire 5" }, - {"P2 Strong Kick" , BIT_DIGITAL , CpsXmvSixButtons[1] + 10, "p2 fire 6" }, - - {"Reset" , BIT_DIGITAL , &CpsReset , "reset" }, - {"Diagnostic" , BIT_DIGITAL , CpsInp021+1 , "diag" }, - {"Service" , BIT_DIGITAL , CpsInp021+2 , "service" }, - {"Volume Up" , BIT_DIGITAL , &Cps2VolUp , "volumeup" }, - {"Volume Down" , BIT_DIGITAL , &Cps2VolDwn , "volumedown"}, -}; - -STDINPUTINFO(Xmvsf3v3) - -static struct BurnInputInfo Xmvsf6pInputList[] = { - {"P1 Coin" , BIT_DIGITAL , CpsXmvSixButtons[0] + 12, "p1 coin" }, - {"P1 Start" , BIT_DIGITAL , CpsXmvSixButtons[0] + 11, "p1 start" }, - {"P1 Up" , BIT_DIGITAL , CpsXmvSixButtons[0] + 3 , "p1 up" }, - {"P1 Down" , BIT_DIGITAL , CpsXmvSixButtons[0] + 2 , "p1 down" }, - {"P1 Left" , BIT_DIGITAL , CpsXmvSixButtons[0] + 1 , "p1 left" }, - {"P1 Right" , BIT_DIGITAL , CpsXmvSixButtons[0] + 0 , "p1 right" }, - {"P1 Weak Punch" , BIT_DIGITAL , CpsXmvSixButtons[0] + 4 , "p1 fire 1" }, - {"P1 Medium Punch" , BIT_DIGITAL , CpsXmvSixButtons[0] + 5 , "p1 fire 2" }, - {"P1 Strong Punch" , BIT_DIGITAL , CpsXmvSixButtons[0] + 6 , "p1 fire 3" }, - {"P1 Weak Kick" , BIT_DIGITAL , CpsXmvSixButtons[0] + 8 , "p1 fire 4" }, - {"P1 Medium Kick" , BIT_DIGITAL , CpsXmvSixButtons[0] + 9 , "p1 fire 5" }, - {"P1 Strong Kick" , BIT_DIGITAL , CpsXmvSixButtons[0] + 10, "p1 fire 6" }, - - {"P2 Coin" , BIT_DIGITAL , CpsXmvSixButtons[1] + 12, "p2 coin" }, - {"P2 Start" , BIT_DIGITAL , CpsXmvSixButtons[1] + 11, "p2 start" }, - {"P2 Up" , BIT_DIGITAL , CpsXmvSixButtons[1] + 3 , "p2 up" }, - {"P2 Down" , BIT_DIGITAL , CpsXmvSixButtons[1] + 2 , "p2 down" }, - {"P2 Left" , BIT_DIGITAL , CpsXmvSixButtons[1] + 1 , "p2 left" }, - {"P2 Right" , BIT_DIGITAL , CpsXmvSixButtons[1] + 0 , "p2 right" }, - {"P2 Weak Punch" , BIT_DIGITAL , CpsXmvSixButtons[1] + 4 , "p2 fire 1" }, - {"P2 Medium Punch" , BIT_DIGITAL , CpsXmvSixButtons[1] + 5 , "p2 fire 2" }, - {"P2 Strong Punch" , BIT_DIGITAL , CpsXmvSixButtons[1] + 6 , "p2 fire 3" }, - {"P2 Weak Kick" , BIT_DIGITAL , CpsXmvSixButtons[1] + 8 , "p2 fire 4" }, - {"P2 Medium Kick" , BIT_DIGITAL , CpsXmvSixButtons[1] + 9 , "p2 fire 5" }, - {"P2 Strong Kick" , BIT_DIGITAL , CpsXmvSixButtons[1] + 10, "p2 fire 6" }, - - {"P3 Coin" , BIT_DIGITAL , CpsXmvSixButtons[2] + 12, "p3 coin" }, - {"P3 Start" , BIT_DIGITAL , CpsXmvSixButtons[2] + 11, "p3 start" }, - {"P3 Up" , BIT_DIGITAL , CpsXmvSixButtons[2] + 3 , "p3 up" }, - {"P3 Down" , BIT_DIGITAL , CpsXmvSixButtons[2] + 2 , "p3 down" }, - {"P3 Left" , BIT_DIGITAL , CpsXmvSixButtons[2] + 1 , "p3 left" }, - {"P3 Right" , BIT_DIGITAL , CpsXmvSixButtons[2] + 0 , "p3 right" }, - {"P3 Weak Punch" , BIT_DIGITAL , CpsXmvSixButtons[2] + 4 , "p3 fire 1" }, - {"P3 Medium Punch" , BIT_DIGITAL , CpsXmvSixButtons[2] + 5 , "p3 fire 2" }, - {"P3 Strong Punch" , BIT_DIGITAL , CpsXmvSixButtons[2] + 6 , "p3 fire 3" }, - {"P3 Weak Kick" , BIT_DIGITAL , CpsXmvSixButtons[2] + 8 , "p3 fire 4" }, - {"P3 Medium Kick" , BIT_DIGITAL , CpsXmvSixButtons[2] + 9 , "p3 fire 5" }, - {"P3 Strong Kick" , BIT_DIGITAL , CpsXmvSixButtons[2] + 10, "p3 fire 6" }, - - {"P4 Coin" , BIT_DIGITAL , CpsXmvSixButtons[3] + 12, "p4 coin" }, - {"P4 Start" , BIT_DIGITAL , CpsXmvSixButtons[3] + 11, "p4 start" }, - {"P4 Up" , BIT_DIGITAL , CpsXmvSixButtons[3] + 3 , "p4 up" }, - {"P4 Down" , BIT_DIGITAL , CpsXmvSixButtons[3] + 2 , "p4 down" }, - {"P4 Left" , BIT_DIGITAL , CpsXmvSixButtons[3] + 1 , "p4 left" }, - {"P4 Right" , BIT_DIGITAL , CpsXmvSixButtons[3] + 0 , "p4 right" }, - {"P4 Weak Punch" , BIT_DIGITAL , CpsXmvSixButtons[3] + 4 , "p4 fire 1" }, - {"P4 Medium Punch" , BIT_DIGITAL , CpsXmvSixButtons[3] + 5 , "p4 fire 2" }, - {"P4 Strong Punch" , BIT_DIGITAL , CpsXmvSixButtons[3] + 6 , "p4 fire 3" }, - {"P4 Weak Kick" , BIT_DIGITAL , CpsXmvSixButtons[3] + 8 , "p4 fire 4" }, - {"P4 Medium Kick" , BIT_DIGITAL , CpsXmvSixButtons[3] + 9 , "p4 fire 5" }, - {"P4 Strong Kick" , BIT_DIGITAL , CpsXmvSixButtons[3] + 10, "p4 fire 6" }, - - {"P5 Coin" , BIT_DIGITAL , CpsXmvSixButtons[4] + 12, "p5 coin" }, - {"P5 Start" , BIT_DIGITAL , CpsXmvSixButtons[4] + 11, "p5 start" }, - {"P5 Up" , BIT_DIGITAL , CpsXmvSixButtons[4] + 3 , "p5 up" }, - {"P5 Down" , BIT_DIGITAL , CpsXmvSixButtons[4] + 2 , "p5 down" }, - {"P5 Left" , BIT_DIGITAL , CpsXmvSixButtons[4] + 1 , "p5 left" }, - {"P5 Right" , BIT_DIGITAL , CpsXmvSixButtons[4] + 0 , "p5 right" }, - {"P5 Weak Punch" , BIT_DIGITAL , CpsXmvSixButtons[4] + 4 , "p5 fire 1" }, - {"P5 Medium Punch" , BIT_DIGITAL , CpsXmvSixButtons[4] + 5 , "p5 fire 2" }, - {"P5 Strong Punch" , BIT_DIGITAL , CpsXmvSixButtons[4] + 6 , "p5 fire 3" }, - {"P5 Weak Kick" , BIT_DIGITAL , CpsXmvSixButtons[4] + 8 , "p5 fire 4" }, - {"P5 Medium Kick" , BIT_DIGITAL , CpsXmvSixButtons[4] + 9 , "p5 fire 5" }, - {"P5 Strong Kick" , BIT_DIGITAL , CpsXmvSixButtons[4] + 10, "p5 fire 6" }, - - {"P6 Coin" , BIT_DIGITAL , CpsXmvSixButtons[5] + 12, "p6 coin" }, - {"P6 Start" , BIT_DIGITAL , CpsXmvSixButtons[5] + 11, "p6 start" }, - {"P6 Up" , BIT_DIGITAL , CpsXmvSixButtons[5] + 3 , "p6 up" }, - {"P6 Down" , BIT_DIGITAL , CpsXmvSixButtons[5] + 2 , "p6 down" }, - {"P6 Left" , BIT_DIGITAL , CpsXmvSixButtons[5] + 1 , "p6 left" }, - {"P6 Right" , BIT_DIGITAL , CpsXmvSixButtons[5] + 0 , "p6 right" }, - {"P6 Weak Punch" , BIT_DIGITAL , CpsXmvSixButtons[5] + 4 , "p6 fire 1" }, - {"P6 Medium Punch" , BIT_DIGITAL , CpsXmvSixButtons[5] + 5 , "p6 fire 2" }, - {"P6 Strong Punch" , BIT_DIGITAL , CpsXmvSixButtons[5] + 6 , "p6 fire 3" }, - {"P6 Weak Kick" , BIT_DIGITAL , CpsXmvSixButtons[5] + 8 , "p6 fire 4" }, - {"P6 Medium Kick" , BIT_DIGITAL , CpsXmvSixButtons[5] + 9 , "p6 fire 5" }, - {"P6 Strong Kick" , BIT_DIGITAL , CpsXmvSixButtons[5] + 10, "p6 fire 6" }, - - {"Reset" , BIT_DIGITAL , &CpsReset , "reset" }, - {"Diagnostic" , BIT_DIGITAL , CpsInp021+1 , "diag" }, - {"Service" , BIT_DIGITAL , CpsInp021+2 , "service" }, - {"Volume Up" , BIT_DIGITAL , &Cps2VolUp , "volumeup" }, - {"Volume Down" , BIT_DIGITAL , &Cps2VolDwn , "volumedown"}, -}; - -STDINPUTINFO(Xmvsf6p) - -static INT32 Xmvsf3v3CommonInit(INT32 bWide) -{ - if (CpsXmvSixInit(bWide)) return 1; - - INT32 nRet = Cps2Init(); - if (nRet) { - CpsXmvSixExit(); - return nRet; - } - - CpsRunResetCallbackFunction = CpsXmvSixReset; - CpsMemScanCallbackFunction = CpsXmvSixScan; - - return 0; -} - -static INT32 Xmvsf3v3Init() -{ - return Xmvsf3v3CommonInit(0); -} - -static INT32 Xmvsf3v3WideInit() -{ - return Xmvsf3v3CommonInit(1); -} - -static struct BurnRomInfo Xmvsf3v3RomDesc[] = { - { "xvs3v3.03f", 0x080000, 0x7b151acd, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvse.04f", 0x080000, 0xef015aef, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs.05a", 0x080000, 0x7db6025d, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs.06a", 0x080000, 0xe8e2c75c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs.07", 0x080000, 0x08f0abed, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs3v3.08", 0x080000, 0x477a3f86, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs3v3.09", 0x080000, 0x98e06515, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - - { "xvs3v3.13m", 0x400000, 0xc8b777f6, CPS2_GFX | BRF_GRA }, - { "xvs3v3.15m", 0x400000, 0x8143c2b0, CPS2_GFX | BRF_GRA }, - { "xvs3v3.17m", 0x400000, 0x10cceec9, CPS2_GFX | BRF_GRA }, - { "xvs3v3.19m", 0x400000, 0xf458a1f8, CPS2_GFX | BRF_GRA }, - { "xvs.14m", 0x400000, 0xbcac2e41, CPS2_GFX | BRF_GRA }, - { "xvs.16m", 0x400000, 0xea04a272, CPS2_GFX | BRF_GRA }, - { "xvs.18m", 0x400000, 0xb0def86a, CPS2_GFX | BRF_GRA }, - { "xvs.20m", 0x400000, 0x4b40ff9f, CPS2_GFX | BRF_GRA }, - - { "xvs.01", 0x020000, 0x3999e93a, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - { "xvs.02", 0x020000, 0x101bdee9, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - - { "xvs.11m", 0x200000, 0x9cadcdbc, CPS2_QSND | BRF_SND }, - { "xvs.12m", 0x200000, 0x7b11e460, CPS2_QSND | BRF_SND }, - - { "xmvsf.key", 0x000014, 0xd5c07311, CPS2_ENCRYPTION_KEY }, -}; - -STD_ROM_PICK(Xmvsf3v3) -STD_ROM_FN(Xmvsf3v3) - -static struct BurnRomInfo Xmvsf3v3wRomDesc[] = { - { "xvs3v3w.03f", 0x080000, 0x54ed459e, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvse.04f", 0x080000, 0xef015aef, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs.05a", 0x080000, 0x7db6025d, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs.06a", 0x080000, 0xe8e2c75c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs.07", 0x080000, 0x08f0abed, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs3v3.08", 0x080000, 0x477a3f86, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs3v3w.09", 0x080000, 0x145e92b6, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - - { "xvs3v3.13m", 0x400000, 0xc8b777f6, CPS2_GFX | BRF_GRA }, - { "xvs3v3.15m", 0x400000, 0x8143c2b0, CPS2_GFX | BRF_GRA }, - { "xvs3v3.17m", 0x400000, 0x10cceec9, CPS2_GFX | BRF_GRA }, - { "xvs3v3.19m", 0x400000, 0xf458a1f8, CPS2_GFX | BRF_GRA }, - { "xvs.14m", 0x400000, 0xbcac2e41, CPS2_GFX | BRF_GRA }, - { "xvs.16m", 0x400000, 0xea04a272, CPS2_GFX | BRF_GRA }, - { "xvs.18m", 0x400000, 0xb0def86a, CPS2_GFX | BRF_GRA }, - { "xvs.20m", 0x400000, 0x4b40ff9f, CPS2_GFX | BRF_GRA }, - - { "xvs.01", 0x020000, 0x3999e93a, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - { "xvs.02", 0x020000, 0x101bdee9, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - - { "xvs.11m", 0x200000, 0x9cadcdbc, CPS2_QSND | BRF_SND }, - { "xvs.12m", 0x200000, 0x7b11e460, CPS2_QSND | BRF_SND }, - - { "xmvsf.key", 0x000014, 0xd5c07311, CPS2_ENCRYPTION_KEY }, -}; - -STD_ROM_PICK(Xmvsf3v3w) -STD_ROM_FN(Xmvsf3v3w) - -static struct BurnRomInfo Xmvsf6pRomDesc[] = { - { "xvs6p.03f", 0x080000, 0x8940699a, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvse.04f", 0x080000, 0xef015aef, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs.05a", 0x080000, 0x7db6025d, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs.06a", 0x080000, 0xe8e2c75c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs.07", 0x080000, 0x08f0abed, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs6p.08", 0x080000, 0x14b6a689, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs6p.09", 0x080000, 0x6ea8fdea, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - - { "xvs3v3.13m", 0x400000, 0xc8b777f6, CPS2_GFX | BRF_GRA }, - { "xvs3v3.15m", 0x400000, 0x8143c2b0, CPS2_GFX | BRF_GRA }, - { "xvs3v3.17m", 0x400000, 0x10cceec9, CPS2_GFX | BRF_GRA }, - { "xvs3v3.19m", 0x400000, 0xf458a1f8, CPS2_GFX | BRF_GRA }, - { "xvs.14m", 0x400000, 0xbcac2e41, CPS2_GFX | BRF_GRA }, - { "xvs.16m", 0x400000, 0xea04a272, CPS2_GFX | BRF_GRA }, - { "xvs.18m", 0x400000, 0xb0def86a, CPS2_GFX | BRF_GRA }, - { "xvs.20m", 0x400000, 0x4b40ff9f, CPS2_GFX | BRF_GRA }, - - { "xvs.01", 0x020000, 0x3999e93a, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - { "xvs.02", 0x020000, 0x101bdee9, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - - { "xvs.11m", 0x200000, 0x9cadcdbc, CPS2_QSND | BRF_SND }, - { "xvs.12m", 0x200000, 0x7b11e460, CPS2_QSND | BRF_SND }, - - { "xmvsf.key", 0x000014, 0xd5c07311, CPS2_ENCRYPTION_KEY }, -}; - -STD_ROM_PICK(Xmvsf6p) -STD_ROM_FN(Xmvsf6p) - -static struct BurnRomInfo Xmvsf6pwRomDesc[] = { - { "xvs6pw.03f", 0x080000, 0xa6b836c9, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvse.04f", 0x080000, 0xef015aef, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs.05a", 0x080000, 0x7db6025d, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs.06a", 0x080000, 0xe8e2c75c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs.07", 0x080000, 0x08f0abed, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs6p.08", 0x080000, 0x14b6a689, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "xvs6pw.09", 0x080000, 0xe2160a49, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - - { "xvs3v3.13m", 0x400000, 0xc8b777f6, CPS2_GFX | BRF_GRA }, - { "xvs3v3.15m", 0x400000, 0x8143c2b0, CPS2_GFX | BRF_GRA }, - { "xvs3v3.17m", 0x400000, 0x10cceec9, CPS2_GFX | BRF_GRA }, - { "xvs3v3.19m", 0x400000, 0xf458a1f8, CPS2_GFX | BRF_GRA }, - { "xvs.14m", 0x400000, 0xbcac2e41, CPS2_GFX | BRF_GRA }, - { "xvs.16m", 0x400000, 0xea04a272, CPS2_GFX | BRF_GRA }, - { "xvs.18m", 0x400000, 0xb0def86a, CPS2_GFX | BRF_GRA }, - { "xvs.20m", 0x400000, 0x4b40ff9f, CPS2_GFX | BRF_GRA }, - - { "xvs.01", 0x020000, 0x3999e93a, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - { "xvs.02", 0x020000, 0x101bdee9, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - - { "xvs.11m", 0x200000, 0x9cadcdbc, CPS2_QSND | BRF_SND }, - { "xvs.12m", 0x200000, 0x7b11e460, CPS2_QSND | BRF_SND }, - - { "xmvsf.key", 0x000014, 0xd5c07311, CPS2_ENCRYPTION_KEY }, -}; - -STD_ROM_PICK(Xmvsf6pw) -STD_ROM_FN(Xmvsf6pw) - -struct BurnDriver BurnDrvCpsXmvsf3v3 = { - "xmvsf3v3", "xmvsf", NULL, NULL, "2026", - "X-Men Vs. Street Fighter 3v3 + DHC (Hack, v1.1.0)\0", NULL, "hack (Morphus56K)", "CPS2", - NULL, NULL, NULL, NULL, - BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_SF, - NULL, Xmvsf3v3RomInfo, Xmvsf3v3RomName, NULL, NULL, NULL, NULL, Xmvsf3v3InputInfo, NULL, - Xmvsf3v3Init, DrvExit, Cps2Frame, CpsRedraw, CpsAreaScan, - &CpsRecalcPal, 0x1000, 384, 224, 4, 3 -}; - -struct BurnDriver BurnDrvCpsXmvsf3v3w = { - "xmvsf3v3w", "xmvsf", NULL, NULL, "2026", - "X-Men Vs. Street Fighter 3v3 + DHC (Hack, v1.1.0, wide)\0", NULL, "hack (Morphus56K)", "CPS2", - NULL, NULL, NULL, NULL, - BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_SF, - NULL, Xmvsf3v3wRomInfo, Xmvsf3v3wRomName, NULL, NULL, NULL, NULL, Xmvsf3v3InputInfo, NULL, - Xmvsf3v3WideInit, DrvExit, Cps2Frame, CpsRedraw, CpsAreaScan, - &CpsRecalcPal, 0x1000, 512, 224, 16, 9 -}; - -struct BurnDriver BurnDrvCpsXmvsf6p = { - "xmvsf6p", "xmvsf", NULL, NULL, "2026", - "X-Men Vs. Street Fighter 3v3 + DHC (Hack, v1.1.0, 6 players)\0", NULL, "hack (Morphus56K)", "CPS2", - NULL, NULL, NULL, NULL, - BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 6, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_SF, - NULL, Xmvsf6pRomInfo, Xmvsf6pRomName, NULL, NULL, NULL, NULL, Xmvsf6pInputInfo, NULL, - Xmvsf3v3Init, DrvExit, Cps2Frame, CpsRedraw, CpsAreaScan, - &CpsRecalcPal, 0x1000, 384, 224, 4, 3 -}; - -struct BurnDriver BurnDrvCpsXmvsf6pw = { - "xmvsf6pw", "xmvsf", NULL, NULL, "2026", - "X-Men Vs. Street Fighter 3v3 + DHC (Hack, v1.1.0, 6 players, wide)\0", NULL, "hack (Morphus56K)", "CPS2", - NULL, NULL, NULL, NULL, - BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 6, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_SF, - NULL, Xmvsf6pwRomInfo, Xmvsf6pwRomName, NULL, NULL, NULL, NULL, Xmvsf6pInputInfo, NULL, - Xmvsf3v3WideInit, DrvExit, Cps2Frame, CpsRedraw, CpsAreaScan, - &CpsRecalcPal, 0x1000, 512, 224, 16, 9 -}; - // Mars Matrix: Hyper Solid Shooting (Hack, Improvement v1.0.0) // 20251204 diff --git a/src/burn/drv/capcom/d_cps2_custom.cpp b/src/burn/drv/capcom/d_cps2_custom.cpp new file mode 100644 index 000000000..0caf68650 --- /dev/null +++ b/src/burn/drv/capcom/d_cps2_custom.cpp @@ -0,0 +1,1630 @@ +#include "cps.h" +#include "xmvsf3v3.h" + +// CPS2 hardware extensions used by custom games. + +struct CpsDisplayState { + char* szPalette; + char* szRegisters; + char* szFrg; + char* szObjects; + char* szEffectivePalette; + bool bRawPalette; +}; +static const CpsDisplayState* pCpsDisplayState = NULL; +static bool CpsPaletteRestored = false; +static UINT16 Cps2PlusPalette[0xc00]; + +// Palette RAM can already contain the next DMA's colors. Save the effective +// palette instead; hardware words can be converted to the current pixel format. +static INT32 CpsPalVisualScan(INT32 nAction) +{ + if (!pCpsDisplayState || !(nAction & ACB_DRIVER_DATA)) return 0; + + struct BurnArea ba; + memset(&ba, 0, sizeof(ba)); + if (!pCpsDisplayState->bRawPalette) { + // Converted colors use the frontend's current pixel format. + ba.Data = CpsPal; + ba.nLen = 0xc00 * sizeof(UINT32); + } else { + ba.Data = Cps2PlusPalette; + ba.nLen = 0xc00 * sizeof(UINT16); + } + ba.szName = pCpsDisplayState->szEffectivePalette; + BurnAcb(&ba); + + if (nAction & ACB_WRITE) { + if (pCpsDisplayState->bRawPalette) { + CpsPalRecalc(Cps2PlusPalette); + } + CpsPaletteRestored = true; + } + + return 0; +} + +// Preserve latched display state so a paused redraw does not read the next frame's RAM. +static INT32 CpsVisualScan(INT32 nAction) +{ + if (!pCpsDisplayState || !(nAction & ACB_DRIVER_DATA)) return 0; + + struct BurnArea ba; + memset(&ba, 0, sizeof(ba)); + ba.Data = CpsSavePal; ba.nLen = 0x2000; ba.szName = pCpsDisplayState->szPalette; + BurnAcb(&ba); + ba.Data = CpsSaveReg[0]; ba.nLen = 0x100 * (MAX_RASTER + 1); ba.szName = pCpsDisplayState->szRegisters; + BurnAcb(&ba); + ba.Data = CpsSaveFrg[0]; ba.nLen = 0x20; ba.szName = pCpsDisplayState->szFrg; + BurnAcb(&ba); + + if (CpsObjScan(nAction, pCpsDisplayState->szObjects)) return 1; + + return CpsPalVisualScan(nAction); +} + +static INT32 CpsPalConsumeRestoredCache() +{ + if (!pCpsDisplayState || !CpsPaletteRestored) return 0; + + CpsPaletteRestored = false; + + return 1; +} + +static INT32 CpsDisplayInit() +{ + CpsObjReset(); + memset(CpsPal, 0, 0xc00 * sizeof(UINT32)); + CpsPaletteRestored = false; + return 0; +} + +static void CpsReplaySoundScan(INT32 nAction, INT32*) +{ + QsndScan(nAction, 1); +} + +static INT32 DrvExit() +{ + pCpsDisplayState = NULL; + CpsPaletteRestored = false; + Cps2Volume = 39; + Cps2DisableDigitalVolume = 0; + return CpsExit(); +} + +static const CpsDisplayState Cps2PlusDisplayState = { + "Latched palette", "Latched registers", "Latched frg", + "Sprite lag buffers", "Effective palette", true +}; + +static UINT8* Cps2PlusObjectBanks = NULL; + +static void Cps2PlusPaletteUpdate(INT32 nPage, const UINT16* pPalette) +{ + // Disabled DMA destinations retain their previous hardware colors. + memcpy(Cps2PlusPalette + 0x200 * nPage, pPalette, 0x200 * sizeof(UINT16)); +} + +static void Cps2PlusObjectGet(UINT8* pExtra, INT32 nEntry, INT32 nSource) +{ + // Blank entries are skipped, so latch the selector with the compacted list. + pExtra[nEntry] = Cps2PlusObjectBanks[((nCpsObjectBank ^ 1) << 10) + nSource]; +} + +static INT32 Cps2PlusObjectTile(UINT16* pObject, INT32 nTile, const UINT8* pExtra, INT32 nEntry) +{ + // Row arithmetic wraps within the selected latched tile bank. + return (nTile & 0xffff) | ((BURN_ENDIAN_SWAP_INT16(pObject[1]) & 0x6000) << 3) | (pExtra[nEntry] << 18); +} + +static INT32 Cps2PlusScroll(INT32 nAddress, INT32 nAttributes, INT32 nLayer) +{ + return ((nAddress + nCpsGfxScroll[nLayer]) & 0x1ffffff) | ((nAttributes & 0x200) << 16); +} + +static UINT32 Cps2PlusObjectOffset(UINT32 a) +{ + // A15 and the bank register choose physical RAM; A13 and A14 mirror its entries. + return (((a >> 15) ^ nCpsObjectBank) & 1) * 0x8000 + (a & 0x1fff); +} + +static UINT8 __fastcall Cps2PlusObjectReadByte(UINT32 a) +{ + return CpsRam708[Cps2PlusObjectOffset(a) ^ 1]; +} + +static UINT16 __fastcall Cps2PlusObjectReadWord(UINT32 a) +{ + return BURN_ENDIAN_SWAP_INT16(*(UINT16*)(CpsRam708 + Cps2PlusObjectOffset(a))); +} + +static void Cps2PlusObjectWrite(UINT32 a) +{ + // A14 is a write selector, not an extra address line in object RAM. + UINT32 nOffset = Cps2PlusObjectOffset(a); + Cps2PlusObjectBanks[(nOffset >> 15) * 0x400 + ((a & 0x1fff) >> 3)] = (a >> 14) & 1; +} + +static void __fastcall Cps2PlusObjectWriteByte(UINT32 a, UINT8 d) +{ + CpsRam708[Cps2PlusObjectOffset(a) ^ 1] = d; + Cps2PlusObjectWrite(a); +} + +static void __fastcall Cps2PlusObjectWriteWord(UINT32 a, UINT16 d) +{ + *(UINT16*)(CpsRam708 + Cps2PlusObjectOffset(a)) = BURN_ENDIAN_SWAP_INT16(d); + Cps2PlusObjectWrite(a); +} + +static void Cps2PlusMapMemory() +{ + SekMapMemory(CpsRom + 0x400000, 0xa00000, 0xdfffff, MAP_ROM); + // Both banks and their mirrors use the same handlers; the register selects physical RAM. + SekMapHandler(4, 0x700000, 0x70ffff, MAP_RAM); + SekSetReadByteHandler(4, Cps2PlusObjectReadByte); + SekSetReadWordHandler(4, Cps2PlusObjectReadWord); + SekSetWriteByteHandler(4, Cps2PlusObjectWriteByte); + SekSetWriteWordHandler(4, Cps2PlusObjectWriteWord); +} + +static UINT8 __fastcall Cps2PlusSoundRead(UINT16 a) +{ + if (a == 0xd007) return (QscRead() & 0x80) | 0x60 | QsndZGetBank(); + return QsndZRead(a); +} + +static void __fastcall Cps2PlusSoundWrite(UINT16 a, UINT8 d) +{ + if (a == 0xd003) { + INT32 nBank = d & 0x1f; + QsndZSetBank(nBank < 30 ? nBank : 0); + return; + } + QsndZWrite(a, d); +} + +static void Cps2PlusSoundInit() +{ + if (nCpsZRomLen == 0x80000) { + ZetOpen(0); + ZetSetReadHandler(Cps2PlusSoundRead); + ZetSetWriteHandler(Cps2PlusSoundWrite); + ZetClose(); + } +} + +static INT32 Cps2PlusReset() +{ + memset(Cps2PlusObjectBanks, 0, 0x800); + CpsObjReset(); + ZetOpen(0); + QsndZReset(); + QscReset(); + ZetClose(); + return 0; +} + +static INT32 Cps2PlusExit() +{ + BurnFree(Cps2PlusObjectBanks); + return DrvExit(); +} + +static INT32 Cps2PlusMemoryScan(INT32 nAction, INT32*) +{ + if (nAction & ACB_DRIVER_DATA) { + struct BurnArea ba; + memset(&ba, 0, sizeof(ba)); + ba.Data = Cps2PlusObjectBanks; + ba.nLen = 0x800; + ba.szName = "CPS2+ object banks"; + BurnAcb(&ba); + if (nAction & ACB_WRITE) { + for (INT32 i = 0; i < 0x800; i++) { + if (Cps2PlusObjectBanks[i] > 1) return 1; + } + } + } + return 0; +} + +static INT32 Cps2PlusScan(INT32 nAction, INT32* pnMin) +{ + INT32 nRet = CpsAreaScan(nAction, pnMin); + return nRet ? nRet : CpsVisualScan(nAction); +} + +static void Cps2PlusFrameStart() +{ + // The restored palette is already current; subsequent DMA updates enabled pages. + if (CpsPalConsumeRestoredCache()) CpsRecalcPal = 0; +} + +// Validate the expanded board before the common CPS2 loader allocates or decodes ROMs. +static INT32 Cps2PlusCheckROMs(UINT32 nSampleMask) +{ + UINT32 nSize[16] = { 0 }; + UINT32 nGfxLane = 0, nGfxSize = 0; + struct BurnRomInfo ri; + + for (INT32 i = 0; !BurnDrvGetRomInfo(&ri, i); i++) { + if (!ri.nLen) continue; + UINT32 nType = ri.nType & 0x0f; + if ((nGfxLane & 3) && nType != CPS2_GFX) return 1; + UINT32 nLimit; + switch (nType) { + case CPS2_PRG_68K: nLimit = 0x800000; break; + case CPS2_GFX: nLimit = 0x4000000; break; + case CPS2_PRG_Z80: nLimit = 0x80000; break; + case CPS2_QSND: nLimit = 0x2000000; break; + case CPS2_ENCRYPTION_KEY: nLimit = 0x14; break; + default: return 1; + } + if (ri.nLen > nLimit - nSize[nType]) return 1; + if ((nType == CPS2_PRG_68K || nType == CPS2_QSND) && (ri.nLen & 1)) return 1; + if (nType == CPS2_ENCRYPTION_KEY && ri.nLen != 0x14) return 1; + if (nType == CPS2_GFX) { + if ((nGfxLane & 3) == 0) { + // The common loader packs smaller graphics groups after larger ones. + if (nGfxSize && ri.nLen > nGfxSize) return 1; + nGfxSize = ri.nLen; + } + if (ri.nLen < 0x80000 || (ri.nLen & 0x7ffff) || ri.nLen != nGfxSize) return 1; + nGfxLane++; + } + nSize[nType] += ri.nLen; + } + + if (nSize[CPS2_PRG_68K] != 0x800000 || nSize[CPS2_GFX] < 0x2000000 || + (nSize[CPS2_PRG_Z80] != 0x40000 && nSize[CPS2_PRG_Z80] != 0x80000) || + nSize[CPS2_ENCRYPTION_KEY] != 0x14 || (nGfxLane & 3)) return 1; + UINT32 nSamples = nSize[CPS2_QSND]; + if (nSamples != 0x800000 && nSamples != 0x1000000 && nSamples != 0x2000000) return 1; + return nSamples <= nSampleMask; +} + +static INT32 Cps2PlusInit(INT32 nSampleBits) +{ + Cps = 2; + nCpsQSamMask = 0; + if (nSampleBits != 23 && nSampleBits != 24 && nSampleBits != 25) return 1; + + UINT32 nSampleMask = (1U << nSampleBits) - 1; + if (Cps2PlusCheckROMs(nSampleMask)) return 1; + + Cps2PlusObjectBanks = (UINT8*)BurnMalloc(0x800); + if (Cps2PlusObjectBanks == NULL) return 1; + memset(Cps2PlusObjectBanks, 0, 0x800); + + pCpsDisplayState = &Cps2PlusDisplayState; + memset(Cps2PlusPalette, 0, sizeof(Cps2PlusPalette)); + CpsPalUpdateCallbackFunction = Cps2PlusPaletteUpdate; + nCpsObjExtraLen = 0x400; + Cps2ObjGetCallbackFunction = Cps2PlusObjectGet; + Cps2ObjTileCallbackFunction = Cps2PlusObjectTile; + CpsRunInitCallbackFunction = CpsDisplayInit; + CpsDrawPaletteCallbackFunction = CpsPalConsumeRestoredCache; + CpsSoundScanCallbackFunction = CpsReplaySoundScan; + bCpsObjectBankHandlers = 1; + CpsMemScanExtraCallbackFunction = Cps2PlusMemoryScan; + Cps2ScrollCallbackFunction = Cps2PlusScroll; + CpsRunResetCallbackFunction = Cps2PlusReset; + CpsRunFrameStartCallbackFunction = Cps2PlusFrameStart; + nCpsQSamMask = nSampleMask; + INT32 nRet = Cps2Init(0x400000, 0x4000000, 0); + if (!nRet) { + SekOpen(0); + Cps2PlusMapMemory(); + SekClose(); + Cps2PlusSoundInit(); + } + return nRet; +} + +// SFZ3 Mix: linear ROM dumps, expanded sprite addresses and external music samples. +static UINT8 AspectDIP; +static UINT8 AspectDIPLast; + +// samples: +// empty + 0x3f L, channel 0, sample + 0x00 +// empty + 0x3f R, channel 1, sample + 0x40 + +static void samples_l_start(int channel, int sample, int loop) +{ + BurnSampleChannelPlay(channel + 0x00, sample + 0x00, loop); +} + +static int samples_l_playing() +{ + return BurnSampleGetChannelStatus(0x00) == SAMPLE_PLAYING; +} +static int samples_r_playing() +{ + return BurnSampleGetChannelStatus(0x01) == SAMPLE_PLAYING; +} + +static void samples_r_start(int channel, int sample, int loop) +{ + BurnSampleChannelPlay(channel + 0x01, sample + 0x40, loop); +} + +// ---------------------------------------------------------------------------- +static void qsound_cps2turbo_write(UINT32 offset, UINT8 data) //Zero800 allows you to write and signal when a new song is played +{ + UINT8 *m_qsound_sharedram1 = CpsZRamC0; + UINT8 *m_qsound_sharedram2 = CpsZRamF0; + if (offset == 0x0001 && (data & 0xff) == 0x00 && m_qsound_sharedram1[0x00] == 0xff) //stop samples sound + { + samples_l_start(0, 0, 0); + samples_r_start(0, 0, 0); + bprintf(0, _T("Stop: %02x to %x\n"), offset, offset); + } + + if (offset == 0xffe) + { + if (m_qsound_sharedram1[0x80] == 0x77) //stop music condition in audiocpu = C080 (z80 hacked) + { + samples_l_start(0, 0, 0); //init L samples + samples_r_start(0, 0, 0); //init R samples + m_qsound_sharedram2[0x27] = 0xc9; //Enable Qsound Volume + m_qsound_sharedram1[0x80] = 0xff; //Reset music id flag + m_qsound_sharedram1[0x81] = 0xff; //Reset music id flag + bprintf(0, _T("Clear: %02x to %x\n"), data, offset); + } + else + if (m_qsound_sharedram1[0x81] > 0x00 && m_qsound_sharedram1[0x81] < 0x40) //play music condition in audiocpu = C081 (z80 hacked) + { + int data_byte = m_qsound_sharedram1[0x81]; + m_qsound_sharedram1[0x80] = 0xff; //Reset music id flag + m_qsound_sharedram1[0x81] = 0xff; //Reset music id flag + bprintf(0, _T("Music: %02x to %x\n"), data_byte, offset); + samples_l_start(0, data_byte, 1); + if (samples_l_playing() == 1) + { + m_qsound_sharedram2[0x27] = 0x00; //Qsound music volume = 0 + } + else + samples_l_start(0, 0, 0); //init L samples + + samples_r_start(0, data_byte, 1); + if (samples_r_playing() == 1) + { + m_qsound_sharedram2[0x27] = 0x00; //Qsound music volume = 0 + } + else + samples_r_start(0, 0, 0); //init R samples + } + else + if (m_qsound_sharedram2[0x27] == 0x00 && samples_l_playing() != 1) //if loadstate and qsound volume = 0 && samples stopped + { + samples_l_start(0, 0, 0); //init L samples + samples_r_start(0, 0, 0); //init R samples + m_qsound_sharedram2[0x27] = 0xc9; //Enable Qsound Volume + m_qsound_sharedram1[0x80] = 0xff; //Reset music id flag + m_qsound_sharedram1[0x81] = 0xff; //Reset music id flag + bprintf(0, _T("Volume0: %02x to %x\n"), data, offset); + } + else + if (m_qsound_sharedram2[0x27] != 0x00 && samples_l_playing() == 1) //if loadstate and qsound volume != 0 && samples playing + { + samples_l_start(0, 0, 0); //init L samples + samples_r_start(0, 0, 0); //init R samples + m_qsound_sharedram2[0x27] = 0xc9; //Enable Qsound Volume + m_qsound_sharedram1[0x80] = 0xff; //Reset music id flag + m_qsound_sharedram1[0x81] = 0xff; //Reset music id flag + bprintf(0, _T("Volume0: %02x to %x\n"), data, offset); + } + else + if ((data & 0xff) == 0x55) //init samples in rom start + { + samples_l_start(0, 0, 0); + samples_r_start(0, 0, 0); + } + } + + m_qsound_sharedram1[offset] = data; //Play data Qsound +} + +static void check_aspect() +{ + if ((AspectDIP & 0x03) != AspectDIPLast) { + INT32 aspects[3][2] = { { 16, 9 }, { 4, 3 }, { 112, 81 } }; + AspectDIPLast = AspectDIP & 0x03; + + INT32 nAspectX, nAspectY; + BurnDrvGetAspect(&nAspectX, &nAspectY); + + if (nAspectX != aspects[AspectDIPLast][0] || nAspectY != aspects[AspectDIPLast][1]) { + bprintf(0, _T("* CPS-2: Changing to %d:%d aspect\n"), aspects[AspectDIPLast][0], aspects[AspectDIPLast][1]); + BurnDrvSetAspect(aspects[AspectDIPLast][0], aspects[AspectDIPLast][1]); + Reinitialise(); + } + } +} + +static void __fastcall Cps2MixWriteByte(UINT32 a, UINT8 d) +{ + if ((a & 0xfffff0) == 0x665000 || (a & 0xfffff0) == 0xfffff0) { + CpsFrg[a & 0x0f] = d; + return; + } + if ((a & 0xfffff0) != 0x400000) CpsWriteByte(a, d); +} + +void __fastcall CPSQSoundC0WriteByte(UINT32 a, UINT8 d); + +static void __fastcall Cps2MixSoundWrite(UINT32 a, UINT8 d) +{ + if (a & 1) qsound_cps2turbo_write((a & 0x1fff) >> 1, d); + CPSQSoundC0WriteByte(a, d); +} + +static void __fastcall Cps2MixRamWriteByte(UINT32 a, UINT8 d) +{ + if (a >= 0xfffff0 && a <= 0xfffffb) CpsFrg[a & 0x0f] = d; + CpsRamFF[(a - 0xff0000) ^ 1] = d; +} + +static void __fastcall Cps2MixRamWriteWord(UINT32 a, UINT16 d) +{ + SEK_DEF_WRITE_WORD(3, a, d); +} + +static void __fastcall Cps2MixSpriteWriteByte(UINT32 a, UINT8 d) +{ + UINT32 nOffset = (a - 0x700000) ^ 1; + CpsRam708[nOffset] = CpsRam708[nOffset + 0x8000] = d; +} + +static void __fastcall Cps2MixSpriteWriteWord(UINT32 a, UINT16 d) +{ + UINT16* pRam = (UINT16*)CpsRam708; + UINT32 nOffset = (a - 0x700000) >> 1; + pRam[nOffset] = pRam[nOffset + 0x4000] = BURN_ENDIAN_SWAP_INT16(d); +} + +static INT32 Cps2MixScroll(INT32 nAddress, INT32, INT32 nLayer) +{ + if (nLayer == 3) nAddress &= ~nCpsGfxScroll[3]; + return nAddress + nCpsGfxScroll[nLayer]; +} + +static INT32 Cps2MixObjectTile(UINT16* pObject, INT32 nTile, const UINT8*, INT32) +{ + if (BURN_ENDIAN_SWAP_INT16(pObject[1]) & 0x1000) pObject[1] |= BURN_ENDIAN_SWAP_INT16(0x8000); + return nTile + ((BURN_ENDIAN_SWAP_INT16(pObject[1]) & 0x8000) << 3); +} + +static void Cps2MixMapMemory() +{ + SekSetWriteByteHandler(0, Cps2MixWriteByte); + SekSetWriteByteHandler(1, Cps2MixSoundWrite); + SekMapHandler(3, 0xff0000, 0xffffff, MAP_WRITE); + SekSetWriteByteHandler(3, Cps2MixRamWriteByte); + SekSetWriteWordHandler(3, Cps2MixRamWriteWord); + SekMapHandler(4, 0x700000, 0x701fff, MAP_WRITE); + SekSetWriteByteHandler(4, Cps2MixSpriteWriteByte); + SekSetWriteWordHandler(4, Cps2MixSpriteWriteWord); +} + +static void Cps2MixSoundInit() +{ + BurnSampleInit(1); + for (INT32 i = 0; i < 0x40; i++) { + BurnSampleSetRoute(i + 0x00, BURN_SND_SAMPLE_ROUTE_1, 0.10, BURN_SND_ROUTE_LEFT); + BurnSampleSetRoute(i + 0x00, BURN_SND_SAMPLE_ROUTE_2, 0.10, BURN_SND_ROUTE_NONE); + BurnSampleSetRoute(i + 0x40, BURN_SND_SAMPLE_ROUTE_1, 0.10, BURN_SND_ROUTE_NONE); + BurnSampleSetRoute(i + 0x40, BURN_SND_SAMPLE_ROUTE_2, 0.10, BURN_SND_ROUTE_RIGHT); + } +} + +static INT32 Cps2MixReset() +{ + AspectDIPLast = 0xff; + check_aspect(); + return 0; +} + +static INT32 Cps2MixExit() +{ + BurnSampleExit(); + return DrvExit(); +} + +static INT32 Cps2MixFrame() +{ + check_aspect(); + INT32 nRet = Cps2Frame(); + if (pBurnSoundOut) BurnSampleRender(pBurnSoundOut, nBurnSoundLen); + return nRet; +} + +static void Cps2MixSoundScan(INT32 nAction, INT32* pnMin) +{ + if (nAction & ACB_DRIVER_DATA) BurnSampleScan(nAction, pnMin); + QsndScan(nAction); +} + +static INT32 Cps2MixLoadTiles() +{ + UINT8* Rom = (UINT8*)BurnMalloc(nCpsGfxLen); + if (Rom == NULL) return 1; + memcpy(Rom, CpsGfx, nCpsGfxLen); + memset(CpsGfx, 0, nCpsGfxLen); + Cps2LoadTilesInterleaved(CpsGfx, Rom, nCpsGfxLen); + BurnFree(Rom); + return 0; +} + +static INT32 Cps2MixInit() +{ + bCpsUseParentBlend = 0; + nQsndZ80Clock = 16000000; + nQsndZBankOffset = 0x10000; + Cps2ScrollCallbackFunction = Cps2MixScroll; + Cps2ObjTileCallbackFunction = Cps2MixObjectTile; + CpsSoundScanCallbackFunction = Cps2MixSoundScan; + CpsRunResetCallbackFunction = Cps2MixReset; + nCPS68KClockspeed = 32000000; + INT32 nRet = Cps2Init(0x400000, 0, 1); + if (!nRet) { + if (Cps2MixLoadTiles()) return 1; + SekOpen(0); + Cps2MixMapMemory(); + SekClose(); + Cps2MixSoundInit(); + } + return nRet; +} + +// XMvsSF 3v3: an input device, extra work RAM and optional wide-screen graphics. +static const CpsDisplayState CpsXmvDisplayState = { + "XMvsSF 3v3 latched palette", "XMvsSF 3v3 latched registers", "XMvsSF 3v3 latched frg", + "XMvsSF 3v3 sprite lag buffers", "XMvsSF 3v3 effective palette", false +}; + +static UINT8 CpsXmvSixButtons[XMV6_PLAYERS][13]; +static INT32 CpsXmvWide; +static UINT16 CpsXmvSixCapabilities = 0; +static XmvSixInputState CpsXmvSixState; +static UINT8 *CpsXmvSixWorkRam = NULL; + +static INT32 CpsXmvSixExit() +{ + CpsXmvWide = 0; + BurnFree(CpsXmvSixWorkRam); + return DrvExit(); +} + +static INT32 CpsXmvSixReset() +{ + XmvSixReset(&CpsXmvSixState); + memset(CpsXmvSixWorkRam, 0, XMV6_WORK_RAM_BYTES); + + return 0; +} + +static INT32 CpsXmvSixScan(INT32 nAction, INT32* pnMin) +{ + INT32 nRet = CpsAreaScan(nAction, pnMin); + if (nRet) return nRet; + + if (nAction & ACB_MEMORY_RAM) { + struct BurnArea ba; + memset(&ba, 0, sizeof(ba)); + ba.Data = CpsXmvSixWorkRam; + ba.nLen = XMV6_WORK_RAM_BYTES; + ba.nAddress = XMV6_WORK_RAM_BASE; + ba.szName = "XMvsSF 3v3 work RAM"; + BurnAcb(&ba); + } + + if (nAction & ACB_DRIVER_DATA) { + SCAN_VAR(CpsXmvSixState); + + if (nAction & ACB_WRITE) { + if (CpsXmvSixState.abi != XMV6_VERSION || CpsXmvSixState.count > XMV6_DEPTH) { + // not a state of this board + CpsXmvSixReset(); + return 1; + } + } + } + + return CpsVisualScan(nAction); +} + +// Wide screen: while the ROM keeps 'XS1X' at 0x813ff8, scroll1 columns 64-127 come from 0x814000 +static const UINT8* CpsXmvScroll1Extension() +{ + const UINT8* tag = CpsXmvSixWorkRam + 0x3ff8; + if (tag[0] != 'X' || tag[1] != 'S' || tag[2] != '1' || tag[3] != 'X') return NULL; + + return CpsXmvSixWorkRam + 0x4000; +} + +// Once a frame, before the input ports are compiled +static INT32 CpsXmvSixSample() +{ + UINT16 raw[XMV6_PLAYERS]; + for (INT32 p = 0; p < XMV6_PLAYERS; p++) { + raw[p] = 0; + for (INT32 bit = 0; bit < 13; bit++) { + raw[p] |= (CpsXmvSixButtons[p][bit] & 1) << bit; + } + } + XmvSixSample(&CpsXmvSixState, raw); + + // P1 and P2 also reach the stock input ports, for the game's own menus + for (INT32 bit = 0; bit < 7; bit++) { + CpsInp001[bit] = (CpsXmvSixState.held[0] >> bit) & 1; + CpsInp000[bit] = (CpsXmvSixState.held[1] >> bit) & 1; + } + for (INT32 p = 0; p < 2; p++) { + UINT16 value = CpsXmvSixState.held[p]; + CpsInp011[p * 4 + 0] = (value >> 8) & 1; // weak kick + CpsInp011[p * 4 + 1] = (value >> 9) & 1; // medium kick + CpsInp020[p + 0] = (value >> 11) & 1; // start + CpsInp020[p + 4] = (value >> 12) & 1; // coin + } + CpsInp011[2] = (CpsXmvSixState.held[0] >> 10) & 1; // P1 strong kick + CpsInp020[6] = (CpsXmvSixState.held[1] >> 10) & 1; // P2 strong kick + return 2; +} + +static UINT8 __fastcall CpsXmvReadByte(UINT32 a) +{ + return XmvSixReadByte(&CpsXmvSixState, CpsXmvSixCapabilities, a - XMV6_BASE); +} + +static UINT16 __fastcall CpsXmvReadWord(UINT32 a) +{ + return XmvSixReadWord(&CpsXmvSixState, CpsXmvSixCapabilities, a - XMV6_BASE); +} + +static UINT8 __fastcall CpsXmvRamReadByte(UINT32 a) +{ + return CpsXmvSixWorkRam[a - XMV6_WORK_RAM_BASE]; +} + +static UINT16 __fastcall CpsXmvRamReadWord(UINT32 a) +{ + return XmvSixRamReadWord(CpsXmvSixWorkRam, a - XMV6_WORK_RAM_BASE); +} + +static void __fastcall CpsXmvRamWriteByte(UINT32 a, UINT8 d) +{ + CpsXmvSixWorkRam[a - XMV6_WORK_RAM_BASE] = d; +} + +static void __fastcall CpsXmvRamWriteWord(UINT32 a, UINT16 d) +{ + XmvSixRamWriteWord(CpsXmvSixWorkRam, a - XMV6_WORK_RAM_BASE, d); +} + +static void CpsXmvMapMemory() +{ + // Instruction reads use the same device decode as data reads. + SekMapHandler(5, XMV6_BASE, XMV6_BASE + XMV6_WINDOW_BYTES - 1, MAP_ROM); + SekSetReadByteHandler(5, CpsXmvReadByte); + SekSetReadWordHandler(5, CpsXmvReadWord); + SekMapHandler(6, XMV6_WORK_RAM_BASE, XMV6_WORK_RAM_BASE + XMV6_WORK_RAM_BYTES - 1, MAP_RAM); + SekSetReadByteHandler(6, CpsXmvRamReadByte); + SekSetReadWordHandler(6, CpsXmvRamReadWord); + SekSetWriteByteHandler(6, CpsXmvRamWriteByte); + SekSetWriteWordHandler(6, CpsXmvRamWriteWord); +} + +static INT32 CpsXmvObjectPalette(INT32 n, UINT16 x, UINT16 y, INT32 a, UINT32* pPalette) +{ + UINT32 nColor; + if (!XmvPortraitObject(n, x, y, a, CpsXmvWide != 0, &nColor)) return 0; + UINT16 nColors[16]; + if (!XmvPortraitColors(CpsRom, nCpsRomLen, n, nColor, nColors)) return -1; + for (INT32 pen = 0; pen < 16; pen++) { + UINT32 c = nColors[pen]; + pPalette[pen] = BurnHighCol(((c >> 8) & 15) * 17, ((c >> 4) & 15) * 17, (c & 15) * 17, 0); + } + return 1; +} + +static INT32 CpsXmvSixInit(INT32 bWide) +{ + CpsXmvSixWorkRam = (UINT8*)BurnMalloc(XMV6_WORK_RAM_BYTES); + if (CpsXmvSixWorkRam == NULL) return 1; + + CpsXmvWide = bWide ? 1 : 0; + CpsXmvSixCapabilities = XMV6_CAP_WORK_RAM | XMV6_CAP_PORTRAITS | XMV6_CAP_PORTRAITS_6 | XMV6_CAP_PORTRAITS_12 | XMV6_CAP_PORTRAITS_18; + if (bWide) CpsXmvSixCapabilities |= XMV6_CAP_WIDE | XMV6_CAP_WIDE_SCROLL1; + memset(CpsXmvSixButtons, 0, sizeof(CpsXmvSixButtons)); + CpsXmvSixReset(); + + pCpsDisplayState = &CpsXmvDisplayState; + CpsRunInitCallbackFunction = CpsDisplayInit; + CpsDrawPaletteCallbackFunction = CpsPalConsumeRestoredCache; + CpsSoundScanCallbackFunction = CpsReplaySoundScan; + CpsRwGetInpCallbackFunction = CpsXmvSixSample; + Cps2Scroll1CallbackFunction = bWide ? CpsXmvScroll1Extension : NULL; + Cps2ObjPaletteCallbackFunction = CpsXmvObjectPalette; + CpsRunResetCallbackFunction = CpsXmvSixReset; + INT32 nRet = Cps2Init(); + if (!nRet) { + SekOpen(0); + CpsXmvMapMemory(); + SekClose(); + } + return nRet; +} + +// Input definitions + +static struct BurnInputInfo Cps2CustomInputList[] = { + {"P1 Coin" , BIT_DIGITAL , CpsInp020+4, "p1 coin" }, + {"P1 Start" , BIT_DIGITAL , CpsInp020+0, "p1 start" }, + {"P1 Up" , BIT_DIGITAL , CpsInp001+3, "p1 up" }, + {"P1 Down" , BIT_DIGITAL , CpsInp001+2, "p1 down" }, + {"P1 Left" , BIT_DIGITAL , CpsInp001+1, "p1 left" }, + {"P1 Right" , BIT_DIGITAL , CpsInp001+0, "p1 right" }, + {"P1 Weak Punch" , BIT_DIGITAL , CpsInp001+4, "p1 fire 1" }, + {"P1 Medium Punch" , BIT_DIGITAL , CpsInp001+5, "p1 fire 2" }, + {"P1 Strong Punch" , BIT_DIGITAL , CpsInp001+6, "p1 fire 3" }, + {"P1 Weak Kick" , BIT_DIGITAL , CpsInp011+0, "p1 fire 4" }, + {"P1 Medium Kick" , BIT_DIGITAL , CpsInp011+1, "p1 fire 5" }, + {"P1 Strong Kick" , BIT_DIGITAL , CpsInp011+2, "p1 fire 6" }, + + {"P2 Coin" , BIT_DIGITAL , CpsInp020+5, "p2 coin" }, + {"P2 Start" , BIT_DIGITAL , CpsInp020+1, "p2 start" }, + {"P2 Up" , BIT_DIGITAL , CpsInp000+3, "p2 up" }, + {"P2 Down" , BIT_DIGITAL , CpsInp000+2, "p2 down" }, + {"P2 Left" , BIT_DIGITAL , CpsInp000+1, "p2 left" }, + {"P2 Right" , BIT_DIGITAL , CpsInp000+0, "p2 right" }, + {"P2 Weak Punch" , BIT_DIGITAL , CpsInp000+4, "p2 fire 1" }, + {"P2 Medium Punch" , BIT_DIGITAL , CpsInp000+5, "p2 fire 2" }, + {"P2 Strong Punch" , BIT_DIGITAL , CpsInp000+6, "p2 fire 3" }, + {"P2 Weak Kick" , BIT_DIGITAL , CpsInp011+4, "p2 fire 4" }, + {"P2 Medium Kick" , BIT_DIGITAL , CpsInp011+5, "p2 fire 5" }, + {"P2 Strong Kick" , BIT_DIGITAL , CpsInp020+6, "p2 fire 6" }, + + {"Reset" , BIT_DIGITAL , &CpsReset , "reset" }, + {"Diagnostic" , BIT_DIGITAL , CpsInp021+1, "diag" }, + {"Service" , BIT_DIGITAL , CpsInp021+2, "service" }, + {"Volume Up" , BIT_DIGITAL , &Cps2VolUp , "volumeup" }, + {"Volume Down" , BIT_DIGITAL , &Cps2VolDwn, "volumedown"}, +}; + +STDINPUTINFO(Cps2Custom) + +static struct BurnInputInfo Sfz3mixInputList[] = { + {"P1 Coin" , BIT_DIGITAL , CpsInp020+4, "p1 coin" }, + {"P1 Start" , BIT_DIGITAL , CpsInp020+0, "p1 start" }, + {"P1 Up" , BIT_DIGITAL , CpsInp001+3, "p1 up" }, + {"P1 Down" , BIT_DIGITAL , CpsInp001+2, "p1 down" }, + {"P1 Left" , BIT_DIGITAL , CpsInp001+1, "p1 left" }, + {"P1 Right" , BIT_DIGITAL , CpsInp001+0, "p1 right" }, + {"P1 Weak Punch" , BIT_DIGITAL , CpsInp001+4, "p1 fire 1" }, + {"P1 Medium Punch" , BIT_DIGITAL , CpsInp001+5, "p1 fire 2" }, + {"P1 Strong Punch" , BIT_DIGITAL , CpsInp001+6, "p1 fire 3" }, + {"P1 Weak Kick" , BIT_DIGITAL , CpsInp011+0, "p1 fire 4" }, + {"P1 Medium Kick" , BIT_DIGITAL , CpsInp011+1, "p1 fire 5" }, + {"P1 Strong Kick" , BIT_DIGITAL , CpsInp011+2, "p1 fire 6" }, + + {"P2 Coin" , BIT_DIGITAL , CpsInp020+5, "p2 coin" }, + {"P2 Start" , BIT_DIGITAL , CpsInp020+1, "p2 start" }, + {"P2 Up" , BIT_DIGITAL , CpsInp000+3, "p2 up" }, + {"P2 Down" , BIT_DIGITAL , CpsInp000+2, "p2 down" }, + {"P2 Left" , BIT_DIGITAL , CpsInp000+1, "p2 left" }, + {"P2 Right" , BIT_DIGITAL , CpsInp000+0, "p2 right" }, + {"P2 Weak Punch" , BIT_DIGITAL , CpsInp000+4, "p2 fire 1" }, + {"P2 Medium Punch" , BIT_DIGITAL , CpsInp000+5, "p2 fire 2" }, + {"P2 Strong Punch" , BIT_DIGITAL , CpsInp000+6, "p2 fire 3" }, + {"P2 Weak Kick" , BIT_DIGITAL , CpsInp011+4, "p2 fire 4" }, + {"P2 Medium Kick" , BIT_DIGITAL , CpsInp011+5, "p2 fire 5" }, + {"P2 Strong Kick" , BIT_DIGITAL , CpsInp020+6, "p2 fire 6" }, + + {"Reset" , BIT_DIGITAL , &CpsReset , "reset" }, + {"Diagnostic" , BIT_DIGITAL , CpsInp021+1, "diag" }, + {"Service" , BIT_DIGITAL , CpsInp021+2, "service" }, + {"Volume Up" , BIT_DIGITAL , &Cps2VolUp , "volumeup" }, + {"Volume Down" , BIT_DIGITAL , &Cps2VolDwn, "volumedown"}, + {"Dip A" , BIT_DIPSWITCH, &AspectDIP , "dip" }, +}; + +STDINPUTINFO(Sfz3mix) + +// sfz3mix 0.20+ only! +static struct BurnDIPInfo Sfz3mixDIPList[] = +{ + DIP_OFFSET(0x1d) + {0x00, 0xff, 0xff, 0x00, NULL }, + + {0 , 0xfe, 0 , 3, "Aspect Ratio" }, + {0x00, 0x01, 0x03, 0x00, "16:9" }, + {0x00, 0x01, 0x03, 0x01, "4:3" }, + {0x00, 0x01, 0x03, 0x02, "112:81" }, +}; + +STDDIPINFO(Sfz3mix) + + +// Street Fighter Zero 3 Mix v0.32 (hacked by Zero800) +// https://sfz3mix.github.io/ + +static struct BurnSampleInfo Sfz3mixSampleDesc[] = { + { "empty", SAMPLE_NOLOOP }, + { "L01", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L02", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L03", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L04", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L05", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L06", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L07", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L08", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L09", SAMPLE_NOLOOP }, + { "L0A", SAMPLE_NOLOOP }, + { "L0B", SAMPLE_NOLOOP }, + { "L0C", SAMPLE_NOLOOP }, + { "L0D", SAMPLE_NOLOOP }, + { "L0E", SAMPLE_NOLOOP }, + { "L0F", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L10", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L11", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L12", SAMPLE_NOLOOP }, + { "L13", SAMPLE_NOLOOP }, + { "L14", SAMPLE_NOLOOP }, + { "L15", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L16", SAMPLE_NOLOOP }, + { "L17", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L18", SAMPLE_NOLOOP }, + { "L19", SAMPLE_NOLOOP }, + { "L1A", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L1B", SAMPLE_NOLOOP }, + { "L1C", SAMPLE_NOLOOP }, + { "L1D", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L1E", SAMPLE_NOLOOP }, + { "L1F", SAMPLE_NOLOOP }, + { "L20", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L21", SAMPLE_NOLOOP }, + { "L22", SAMPLE_NOLOOP }, + { "L23", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L24", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L25", SAMPLE_NOLOOP }, + { "L26", SAMPLE_NOLOOP }, + { "L27", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L28", SAMPLE_NOLOOP }, + { "L29", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L2A", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L2B", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L2C", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L2D", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L2E", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L2F", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L30", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L31", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L32", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L33", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L34", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L35", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L36", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L37", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L38", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L39", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L3A", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L3B", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L3C", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L3D", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L3E", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "L3F", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "empty", SAMPLE_NOLOOP }, + { "R01", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R02", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R03", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R04", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R05", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R06", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R07", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R08", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R09", SAMPLE_NOLOOP }, + { "R0A", SAMPLE_NOLOOP }, + { "R0B", SAMPLE_NOLOOP }, + { "R0C", SAMPLE_NOLOOP }, + { "R0D", SAMPLE_NOLOOP }, + { "R0E", SAMPLE_NOLOOP }, + { "R0F", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R10", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R11", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R12", SAMPLE_NOLOOP }, + { "R13", SAMPLE_NOLOOP }, + { "R14", SAMPLE_NOLOOP }, + { "R15", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R16", SAMPLE_NOLOOP }, + { "R17", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R18", SAMPLE_NOLOOP }, + { "R19", SAMPLE_NOLOOP }, + { "R1A", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R1B", SAMPLE_NOLOOP }, + { "R1C", SAMPLE_NOLOOP }, + { "R1D", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R1E", SAMPLE_NOLOOP }, + { "R1F", SAMPLE_NOLOOP }, + { "R20", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R21", SAMPLE_NOLOOP }, + { "R22", SAMPLE_NOLOOP }, + { "R23", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R24", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R25", SAMPLE_NOLOOP }, + { "R26", SAMPLE_NOLOOP }, + { "R27", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R28", SAMPLE_NOLOOP }, + { "R29", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R2A", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R2B", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R2C", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R2D", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R2E", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R2F", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R30", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R31", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R32", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R33", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R34", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R35", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R36", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R37", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R38", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R39", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R3A", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R3B", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R3C", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R3D", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R3E", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "R3F", SAMPLE_NOLOOP | SAMPLE_NODUMP }, + { "", 0 } +}; + +STD_SAMPLE_PICK(Sfz3mix) +STD_SAMPLE_FN(Sfz3mix) + +static struct BurnRomInfo Sfz3mixRomDesc[] = { + { "sz3mx.03", 0x0600000, 0x7a968da6, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + + { "sz3mx.13m", 0x2000000, 0x5389b651, CPS2_GFX | BRF_GRA }, + { "sz3mx.21m", 0x2000000, 0x914c7031, CPS2_GFX | BRF_GRA }, + + { "sz3mx.01", 0x0080000, 0x52f98a5c, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + + { "sz3mx.11m", 0x1000000, 0x395edce2, CPS2_QSND | BRF_SND }, + + { "phoenix.key", 0x0000014, 0x2cf772b0, CPS2_ENCRYPTION_KEY }, +}; + +STD_ROM_PICK(Sfz3mix) +STD_ROM_FN(Sfz3mix) + +struct BurnDriver BurnDrvCpsSfz3mix = { + "sfz3mix", "sfa3", NULL, "sfz3mix", "2026", + "Street Fighter Zero 3 Mix v0.32\0", NULL, "hack (zero800)", "CPS2", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK | BDF_HISCORE_SUPPORTED, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_SF, + NULL, Sfz3mixRomInfo, Sfz3mixRomName, NULL, NULL, Sfz3mixSampleInfo, Sfz3mixSampleName, Sfz3mixInputInfo, Sfz3mixDIPInfo, + Cps2MixInit, Cps2MixExit, Cps2MixFrame, CpsRedraw, CpsAreaScan, + &CpsRecalcPal, 0x1000, 416, 234, 16, 9 +}; + + +// X-Men Vs. Street Fighter 3v3 + DHC (Hack, v1.1.0) +// Modified by Morphus56K +// 2026-10-11 +// https://www.morphus56k.com/blog/xmvsf-three-on-three +// Three characters a team, Delayed Hyper Combos, snapbacks and eighteen colors per character. +// The 6 player sets put P1-P3 against P4-P6; the wide sets show a 512 pixel wide arena. +// All four need the board additions in xmvsf3v3.h (inputs for six players, extra work RAM, +// HUD portraits in the chosen color). + +static struct BurnInputInfo Xmvsf3v3InputList[] = { + {"P1 Coin" , BIT_DIGITAL , CpsXmvSixButtons[0] + 12, "p1 coin" }, + {"P1 Start" , BIT_DIGITAL , CpsXmvSixButtons[0] + 11, "p1 start" }, + {"P1 Up" , BIT_DIGITAL , CpsXmvSixButtons[0] + 3 , "p1 up" }, + {"P1 Down" , BIT_DIGITAL , CpsXmvSixButtons[0] + 2 , "p1 down" }, + {"P1 Left" , BIT_DIGITAL , CpsXmvSixButtons[0] + 1 , "p1 left" }, + {"P1 Right" , BIT_DIGITAL , CpsXmvSixButtons[0] + 0 , "p1 right" }, + {"P1 Weak Punch" , BIT_DIGITAL , CpsXmvSixButtons[0] + 4 , "p1 fire 1" }, + {"P1 Medium Punch" , BIT_DIGITAL , CpsXmvSixButtons[0] + 5 , "p1 fire 2" }, + {"P1 Strong Punch" , BIT_DIGITAL , CpsXmvSixButtons[0] + 6 , "p1 fire 3" }, + {"P1 Weak Kick" , BIT_DIGITAL , CpsXmvSixButtons[0] + 8 , "p1 fire 4" }, + {"P1 Medium Kick" , BIT_DIGITAL , CpsXmvSixButtons[0] + 9 , "p1 fire 5" }, + {"P1 Strong Kick" , BIT_DIGITAL , CpsXmvSixButtons[0] + 10, "p1 fire 6" }, + + {"P2 Coin" , BIT_DIGITAL , CpsXmvSixButtons[1] + 12, "p2 coin" }, + {"P2 Start" , BIT_DIGITAL , CpsXmvSixButtons[1] + 11, "p2 start" }, + {"P2 Up" , BIT_DIGITAL , CpsXmvSixButtons[1] + 3 , "p2 up" }, + {"P2 Down" , BIT_DIGITAL , CpsXmvSixButtons[1] + 2 , "p2 down" }, + {"P2 Left" , BIT_DIGITAL , CpsXmvSixButtons[1] + 1 , "p2 left" }, + {"P2 Right" , BIT_DIGITAL , CpsXmvSixButtons[1] + 0 , "p2 right" }, + {"P2 Weak Punch" , BIT_DIGITAL , CpsXmvSixButtons[1] + 4 , "p2 fire 1" }, + {"P2 Medium Punch" , BIT_DIGITAL , CpsXmvSixButtons[1] + 5 , "p2 fire 2" }, + {"P2 Strong Punch" , BIT_DIGITAL , CpsXmvSixButtons[1] + 6 , "p2 fire 3" }, + {"P2 Weak Kick" , BIT_DIGITAL , CpsXmvSixButtons[1] + 8 , "p2 fire 4" }, + {"P2 Medium Kick" , BIT_DIGITAL , CpsXmvSixButtons[1] + 9 , "p2 fire 5" }, + {"P2 Strong Kick" , BIT_DIGITAL , CpsXmvSixButtons[1] + 10, "p2 fire 6" }, + + {"Reset" , BIT_DIGITAL , &CpsReset , "reset" }, + {"Diagnostic" , BIT_DIGITAL , CpsInp021+1 , "diag" }, + {"Service" , BIT_DIGITAL , CpsInp021+2 , "service" }, + {"Volume Up" , BIT_DIGITAL , &Cps2VolUp , "volumeup" }, + {"Volume Down" , BIT_DIGITAL , &Cps2VolDwn , "volumedown"}, +}; + +STDINPUTINFO(Xmvsf3v3) + +static struct BurnInputInfo Xmvsf6pInputList[] = { + {"P1 Coin" , BIT_DIGITAL , CpsXmvSixButtons[0] + 12, "p1 coin" }, + {"P1 Start" , BIT_DIGITAL , CpsXmvSixButtons[0] + 11, "p1 start" }, + {"P1 Up" , BIT_DIGITAL , CpsXmvSixButtons[0] + 3 , "p1 up" }, + {"P1 Down" , BIT_DIGITAL , CpsXmvSixButtons[0] + 2 , "p1 down" }, + {"P1 Left" , BIT_DIGITAL , CpsXmvSixButtons[0] + 1 , "p1 left" }, + {"P1 Right" , BIT_DIGITAL , CpsXmvSixButtons[0] + 0 , "p1 right" }, + {"P1 Weak Punch" , BIT_DIGITAL , CpsXmvSixButtons[0] + 4 , "p1 fire 1" }, + {"P1 Medium Punch" , BIT_DIGITAL , CpsXmvSixButtons[0] + 5 , "p1 fire 2" }, + {"P1 Strong Punch" , BIT_DIGITAL , CpsXmvSixButtons[0] + 6 , "p1 fire 3" }, + {"P1 Weak Kick" , BIT_DIGITAL , CpsXmvSixButtons[0] + 8 , "p1 fire 4" }, + {"P1 Medium Kick" , BIT_DIGITAL , CpsXmvSixButtons[0] + 9 , "p1 fire 5" }, + {"P1 Strong Kick" , BIT_DIGITAL , CpsXmvSixButtons[0] + 10, "p1 fire 6" }, + + {"P2 Coin" , BIT_DIGITAL , CpsXmvSixButtons[1] + 12, "p2 coin" }, + {"P2 Start" , BIT_DIGITAL , CpsXmvSixButtons[1] + 11, "p2 start" }, + {"P2 Up" , BIT_DIGITAL , CpsXmvSixButtons[1] + 3 , "p2 up" }, + {"P2 Down" , BIT_DIGITAL , CpsXmvSixButtons[1] + 2 , "p2 down" }, + {"P2 Left" , BIT_DIGITAL , CpsXmvSixButtons[1] + 1 , "p2 left" }, + {"P2 Right" , BIT_DIGITAL , CpsXmvSixButtons[1] + 0 , "p2 right" }, + {"P2 Weak Punch" , BIT_DIGITAL , CpsXmvSixButtons[1] + 4 , "p2 fire 1" }, + {"P2 Medium Punch" , BIT_DIGITAL , CpsXmvSixButtons[1] + 5 , "p2 fire 2" }, + {"P2 Strong Punch" , BIT_DIGITAL , CpsXmvSixButtons[1] + 6 , "p2 fire 3" }, + {"P2 Weak Kick" , BIT_DIGITAL , CpsXmvSixButtons[1] + 8 , "p2 fire 4" }, + {"P2 Medium Kick" , BIT_DIGITAL , CpsXmvSixButtons[1] + 9 , "p2 fire 5" }, + {"P2 Strong Kick" , BIT_DIGITAL , CpsXmvSixButtons[1] + 10, "p2 fire 6" }, + + {"P3 Coin" , BIT_DIGITAL , CpsXmvSixButtons[2] + 12, "p3 coin" }, + {"P3 Start" , BIT_DIGITAL , CpsXmvSixButtons[2] + 11, "p3 start" }, + {"P3 Up" , BIT_DIGITAL , CpsXmvSixButtons[2] + 3 , "p3 up" }, + {"P3 Down" , BIT_DIGITAL , CpsXmvSixButtons[2] + 2 , "p3 down" }, + {"P3 Left" , BIT_DIGITAL , CpsXmvSixButtons[2] + 1 , "p3 left" }, + {"P3 Right" , BIT_DIGITAL , CpsXmvSixButtons[2] + 0 , "p3 right" }, + {"P3 Weak Punch" , BIT_DIGITAL , CpsXmvSixButtons[2] + 4 , "p3 fire 1" }, + {"P3 Medium Punch" , BIT_DIGITAL , CpsXmvSixButtons[2] + 5 , "p3 fire 2" }, + {"P3 Strong Punch" , BIT_DIGITAL , CpsXmvSixButtons[2] + 6 , "p3 fire 3" }, + {"P3 Weak Kick" , BIT_DIGITAL , CpsXmvSixButtons[2] + 8 , "p3 fire 4" }, + {"P3 Medium Kick" , BIT_DIGITAL , CpsXmvSixButtons[2] + 9 , "p3 fire 5" }, + {"P3 Strong Kick" , BIT_DIGITAL , CpsXmvSixButtons[2] + 10, "p3 fire 6" }, + + {"P4 Coin" , BIT_DIGITAL , CpsXmvSixButtons[3] + 12, "p4 coin" }, + {"P4 Start" , BIT_DIGITAL , CpsXmvSixButtons[3] + 11, "p4 start" }, + {"P4 Up" , BIT_DIGITAL , CpsXmvSixButtons[3] + 3 , "p4 up" }, + {"P4 Down" , BIT_DIGITAL , CpsXmvSixButtons[3] + 2 , "p4 down" }, + {"P4 Left" , BIT_DIGITAL , CpsXmvSixButtons[3] + 1 , "p4 left" }, + {"P4 Right" , BIT_DIGITAL , CpsXmvSixButtons[3] + 0 , "p4 right" }, + {"P4 Weak Punch" , BIT_DIGITAL , CpsXmvSixButtons[3] + 4 , "p4 fire 1" }, + {"P4 Medium Punch" , BIT_DIGITAL , CpsXmvSixButtons[3] + 5 , "p4 fire 2" }, + {"P4 Strong Punch" , BIT_DIGITAL , CpsXmvSixButtons[3] + 6 , "p4 fire 3" }, + {"P4 Weak Kick" , BIT_DIGITAL , CpsXmvSixButtons[3] + 8 , "p4 fire 4" }, + {"P4 Medium Kick" , BIT_DIGITAL , CpsXmvSixButtons[3] + 9 , "p4 fire 5" }, + {"P4 Strong Kick" , BIT_DIGITAL , CpsXmvSixButtons[3] + 10, "p4 fire 6" }, + + {"P5 Coin" , BIT_DIGITAL , CpsXmvSixButtons[4] + 12, "p5 coin" }, + {"P5 Start" , BIT_DIGITAL , CpsXmvSixButtons[4] + 11, "p5 start" }, + {"P5 Up" , BIT_DIGITAL , CpsXmvSixButtons[4] + 3 , "p5 up" }, + {"P5 Down" , BIT_DIGITAL , CpsXmvSixButtons[4] + 2 , "p5 down" }, + {"P5 Left" , BIT_DIGITAL , CpsXmvSixButtons[4] + 1 , "p5 left" }, + {"P5 Right" , BIT_DIGITAL , CpsXmvSixButtons[4] + 0 , "p5 right" }, + {"P5 Weak Punch" , BIT_DIGITAL , CpsXmvSixButtons[4] + 4 , "p5 fire 1" }, + {"P5 Medium Punch" , BIT_DIGITAL , CpsXmvSixButtons[4] + 5 , "p5 fire 2" }, + {"P5 Strong Punch" , BIT_DIGITAL , CpsXmvSixButtons[4] + 6 , "p5 fire 3" }, + {"P5 Weak Kick" , BIT_DIGITAL , CpsXmvSixButtons[4] + 8 , "p5 fire 4" }, + {"P5 Medium Kick" , BIT_DIGITAL , CpsXmvSixButtons[4] + 9 , "p5 fire 5" }, + {"P5 Strong Kick" , BIT_DIGITAL , CpsXmvSixButtons[4] + 10, "p5 fire 6" }, + + {"P6 Coin" , BIT_DIGITAL , CpsXmvSixButtons[5] + 12, "p6 coin" }, + {"P6 Start" , BIT_DIGITAL , CpsXmvSixButtons[5] + 11, "p6 start" }, + {"P6 Up" , BIT_DIGITAL , CpsXmvSixButtons[5] + 3 , "p6 up" }, + {"P6 Down" , BIT_DIGITAL , CpsXmvSixButtons[5] + 2 , "p6 down" }, + {"P6 Left" , BIT_DIGITAL , CpsXmvSixButtons[5] + 1 , "p6 left" }, + {"P6 Right" , BIT_DIGITAL , CpsXmvSixButtons[5] + 0 , "p6 right" }, + {"P6 Weak Punch" , BIT_DIGITAL , CpsXmvSixButtons[5] + 4 , "p6 fire 1" }, + {"P6 Medium Punch" , BIT_DIGITAL , CpsXmvSixButtons[5] + 5 , "p6 fire 2" }, + {"P6 Strong Punch" , BIT_DIGITAL , CpsXmvSixButtons[5] + 6 , "p6 fire 3" }, + {"P6 Weak Kick" , BIT_DIGITAL , CpsXmvSixButtons[5] + 8 , "p6 fire 4" }, + {"P6 Medium Kick" , BIT_DIGITAL , CpsXmvSixButtons[5] + 9 , "p6 fire 5" }, + {"P6 Strong Kick" , BIT_DIGITAL , CpsXmvSixButtons[5] + 10, "p6 fire 6" }, + + {"Reset" , BIT_DIGITAL , &CpsReset , "reset" }, + {"Diagnostic" , BIT_DIGITAL , CpsInp021+1 , "diag" }, + {"Service" , BIT_DIGITAL , CpsInp021+2 , "service" }, + {"Volume Up" , BIT_DIGITAL , &Cps2VolUp , "volumeup" }, + {"Volume Down" , BIT_DIGITAL , &Cps2VolDwn , "volumedown"}, +}; + +STDINPUTINFO(Xmvsf6p) + +static INT32 Xmvsf3v3Init() +{ + return CpsXmvSixInit(0); +} + +static INT32 Xmvsf3v3WideInit() +{ + return CpsXmvSixInit(1); +} + +static struct BurnRomInfo Xmvsf3v3RomDesc[] = { + { "xvs3v3.03f", 0x080000, 0x7b151acd, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvse.04f", 0x080000, 0xef015aef, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs.05a", 0x080000, 0x7db6025d, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs.06a", 0x080000, 0xe8e2c75c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs.07", 0x080000, 0x08f0abed, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs3v3.08", 0x080000, 0x477a3f86, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs3v3.09", 0x080000, 0x98e06515, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + + { "xvs3v3.13m", 0x400000, 0xc8b777f6, CPS2_GFX | BRF_GRA }, + { "xvs3v3.15m", 0x400000, 0x8143c2b0, CPS2_GFX | BRF_GRA }, + { "xvs3v3.17m", 0x400000, 0x10cceec9, CPS2_GFX | BRF_GRA }, + { "xvs3v3.19m", 0x400000, 0xf458a1f8, CPS2_GFX | BRF_GRA }, + { "xvs.14m", 0x400000, 0xbcac2e41, CPS2_GFX | BRF_GRA }, + { "xvs.16m", 0x400000, 0xea04a272, CPS2_GFX | BRF_GRA }, + { "xvs.18m", 0x400000, 0xb0def86a, CPS2_GFX | BRF_GRA }, + { "xvs.20m", 0x400000, 0x4b40ff9f, CPS2_GFX | BRF_GRA }, + + { "xvs.01", 0x020000, 0x3999e93a, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "xvs.02", 0x020000, 0x101bdee9, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + + { "xvs.11m", 0x200000, 0x9cadcdbc, CPS2_QSND | BRF_SND }, + { "xvs.12m", 0x200000, 0x7b11e460, CPS2_QSND | BRF_SND }, + + { "xmvsf.key", 0x000014, 0xd5c07311, CPS2_ENCRYPTION_KEY }, +}; + +STD_ROM_PICK(Xmvsf3v3) +STD_ROM_FN(Xmvsf3v3) + +static struct BurnRomInfo Xmvsf3v3wRomDesc[] = { + { "xvs3v3w.03f", 0x080000, 0x54ed459e, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvse.04f", 0x080000, 0xef015aef, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs.05a", 0x080000, 0x7db6025d, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs.06a", 0x080000, 0xe8e2c75c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs.07", 0x080000, 0x08f0abed, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs3v3.08", 0x080000, 0x477a3f86, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs3v3w.09", 0x080000, 0x145e92b6, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + + { "xvs3v3.13m", 0x400000, 0xc8b777f6, CPS2_GFX | BRF_GRA }, + { "xvs3v3.15m", 0x400000, 0x8143c2b0, CPS2_GFX | BRF_GRA }, + { "xvs3v3.17m", 0x400000, 0x10cceec9, CPS2_GFX | BRF_GRA }, + { "xvs3v3.19m", 0x400000, 0xf458a1f8, CPS2_GFX | BRF_GRA }, + { "xvs.14m", 0x400000, 0xbcac2e41, CPS2_GFX | BRF_GRA }, + { "xvs.16m", 0x400000, 0xea04a272, CPS2_GFX | BRF_GRA }, + { "xvs.18m", 0x400000, 0xb0def86a, CPS2_GFX | BRF_GRA }, + { "xvs.20m", 0x400000, 0x4b40ff9f, CPS2_GFX | BRF_GRA }, + + { "xvs.01", 0x020000, 0x3999e93a, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "xvs.02", 0x020000, 0x101bdee9, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + + { "xvs.11m", 0x200000, 0x9cadcdbc, CPS2_QSND | BRF_SND }, + { "xvs.12m", 0x200000, 0x7b11e460, CPS2_QSND | BRF_SND }, + + { "xmvsf.key", 0x000014, 0xd5c07311, CPS2_ENCRYPTION_KEY }, +}; + +STD_ROM_PICK(Xmvsf3v3w) +STD_ROM_FN(Xmvsf3v3w) + +static struct BurnRomInfo Xmvsf6pRomDesc[] = { + { "xvs6p.03f", 0x080000, 0x8940699a, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvse.04f", 0x080000, 0xef015aef, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs.05a", 0x080000, 0x7db6025d, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs.06a", 0x080000, 0xe8e2c75c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs.07", 0x080000, 0x08f0abed, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs6p.08", 0x080000, 0x14b6a689, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs6p.09", 0x080000, 0x6ea8fdea, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + + { "xvs3v3.13m", 0x400000, 0xc8b777f6, CPS2_GFX | BRF_GRA }, + { "xvs3v3.15m", 0x400000, 0x8143c2b0, CPS2_GFX | BRF_GRA }, + { "xvs3v3.17m", 0x400000, 0x10cceec9, CPS2_GFX | BRF_GRA }, + { "xvs3v3.19m", 0x400000, 0xf458a1f8, CPS2_GFX | BRF_GRA }, + { "xvs.14m", 0x400000, 0xbcac2e41, CPS2_GFX | BRF_GRA }, + { "xvs.16m", 0x400000, 0xea04a272, CPS2_GFX | BRF_GRA }, + { "xvs.18m", 0x400000, 0xb0def86a, CPS2_GFX | BRF_GRA }, + { "xvs.20m", 0x400000, 0x4b40ff9f, CPS2_GFX | BRF_GRA }, + + { "xvs.01", 0x020000, 0x3999e93a, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "xvs.02", 0x020000, 0x101bdee9, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + + { "xvs.11m", 0x200000, 0x9cadcdbc, CPS2_QSND | BRF_SND }, + { "xvs.12m", 0x200000, 0x7b11e460, CPS2_QSND | BRF_SND }, + + { "xmvsf.key", 0x000014, 0xd5c07311, CPS2_ENCRYPTION_KEY }, +}; + +STD_ROM_PICK(Xmvsf6p) +STD_ROM_FN(Xmvsf6p) + +static struct BurnRomInfo Xmvsf6pwRomDesc[] = { + { "xvs6pw.03f", 0x080000, 0xa6b836c9, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvse.04f", 0x080000, 0xef015aef, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs.05a", 0x080000, 0x7db6025d, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs.06a", 0x080000, 0xe8e2c75c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs.07", 0x080000, 0x08f0abed, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs6p.08", 0x080000, 0x14b6a689, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "xvs6pw.09", 0x080000, 0xe2160a49, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + + { "xvs3v3.13m", 0x400000, 0xc8b777f6, CPS2_GFX | BRF_GRA }, + { "xvs3v3.15m", 0x400000, 0x8143c2b0, CPS2_GFX | BRF_GRA }, + { "xvs3v3.17m", 0x400000, 0x10cceec9, CPS2_GFX | BRF_GRA }, + { "xvs3v3.19m", 0x400000, 0xf458a1f8, CPS2_GFX | BRF_GRA }, + { "xvs.14m", 0x400000, 0xbcac2e41, CPS2_GFX | BRF_GRA }, + { "xvs.16m", 0x400000, 0xea04a272, CPS2_GFX | BRF_GRA }, + { "xvs.18m", 0x400000, 0xb0def86a, CPS2_GFX | BRF_GRA }, + { "xvs.20m", 0x400000, 0x4b40ff9f, CPS2_GFX | BRF_GRA }, + + { "xvs.01", 0x020000, 0x3999e93a, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "xvs.02", 0x020000, 0x101bdee9, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + + { "xvs.11m", 0x200000, 0x9cadcdbc, CPS2_QSND | BRF_SND }, + { "xvs.12m", 0x200000, 0x7b11e460, CPS2_QSND | BRF_SND }, + + { "xmvsf.key", 0x000014, 0xd5c07311, CPS2_ENCRYPTION_KEY }, +}; + +STD_ROM_PICK(Xmvsf6pw) +STD_ROM_FN(Xmvsf6pw) + +struct BurnDriver BurnDrvCpsXmvsf3v3 = { + "xmvsf3v3", "xmvsf", NULL, NULL, "2026", + "X-Men Vs. Street Fighter 3v3 + DHC (Hack, v1.1.0)\0", NULL, "hack (Morphus56K)", "CPS2", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_SF, + NULL, Xmvsf3v3RomInfo, Xmvsf3v3RomName, NULL, NULL, NULL, NULL, Xmvsf3v3InputInfo, NULL, + Xmvsf3v3Init, CpsXmvSixExit, Cps2Frame, CpsRedraw, CpsXmvSixScan, + &CpsRecalcPal, 0x1000, 384, 224, 4, 3 +}; + +struct BurnDriver BurnDrvCpsXmvsf3v3w = { + "xmvsf3v3w", "xmvsf", NULL, NULL, "2026", + "X-Men Vs. Street Fighter 3v3 + DHC (Hack, v1.1.0, wide)\0", NULL, "hack (Morphus56K)", "CPS2", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_SF, + NULL, Xmvsf3v3wRomInfo, Xmvsf3v3wRomName, NULL, NULL, NULL, NULL, Xmvsf3v3InputInfo, NULL, + Xmvsf3v3WideInit, CpsXmvSixExit, Cps2Frame, CpsRedraw, CpsXmvSixScan, + &CpsRecalcPal, 0x1000, 512, 224, 16, 9 +}; + +struct BurnDriver BurnDrvCpsXmvsf6p = { + "xmvsf6p", "xmvsf", NULL, NULL, "2026", + "X-Men Vs. Street Fighter 3v3 + DHC (Hack, v1.1.0, 6 players)\0", NULL, "hack (Morphus56K)", "CPS2", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 6, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_SF, + NULL, Xmvsf6pRomInfo, Xmvsf6pRomName, NULL, NULL, NULL, NULL, Xmvsf6pInputInfo, NULL, + Xmvsf3v3Init, CpsXmvSixExit, Cps2Frame, CpsRedraw, CpsXmvSixScan, + &CpsRecalcPal, 0x1000, 384, 224, 4, 3 +}; + +struct BurnDriver BurnDrvCpsXmvsf6pw = { + "xmvsf6pw", "xmvsf", NULL, NULL, "2026", + "X-Men Vs. Street Fighter 3v3 + DHC (Hack, v1.1.0, 6 players, wide)\0", NULL, "hack (Morphus56K)", "CPS2", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 6, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_SF, + NULL, Xmvsf6pwRomInfo, Xmvsf6pwRomName, NULL, NULL, NULL, NULL, Xmvsf6pInputInfo, NULL, + Xmvsf3v3WideInit, CpsXmvSixExit, Cps2Frame, CpsRedraw, CpsXmvSixScan, + &CpsRecalcPal, 0x1000, 512, 224, 16, 9 +}; + + +static INT32 VampireArrangeInit() +{ + return Cps2PlusInit(24); +} + +// Vampire Savior Arrange (Hack, English RC3) + +static struct BurnRomInfo Vsavs01RomDesc[] = { + { "c71s01.p1", 0x080000, 0x33bd7728, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s01.p2", 0x080000, 0x23d6fc9b, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s01.p3", 0x080000, 0xf0dbcf28, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "vm3j.06b", 0x080000, 0x2c4297e0, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s01.p5", 0x080000, 0x7af80c9c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "vm3j.08a", 0x080000, 0x5773e5c9, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s01.p7", 0x080000, 0x6428a025, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s01.p8", 0x080000, 0x29316daa, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s01.p9", 0x400000, 0xa7e15abc, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + + { "c71s01.c1", 0x400000, 0x2eb67fcc, CPS2_GFX | BRF_GRA }, + { "c71s01.c2", 0x400000, 0xaee349f0, CPS2_GFX | BRF_GRA }, + { "c71s01.c3", 0x400000, 0x0e345bb8, CPS2_GFX | BRF_GRA }, + { "c71s01.c4", 0x400000, 0x33faf930, CPS2_GFX | BRF_GRA }, + { "vm3.14m", 0x400000, 0xc1a28e6c, CPS2_GFX | BRF_GRA }, + { "vm3.16m", 0x400000, 0x194a7304, CPS2_GFX | BRF_GRA }, + { "vm3.18m", 0x400000, 0xdf9a9f47, CPS2_GFX | BRF_GRA }, + { "vm3.20m", 0x400000, 0xc22fc3d9, CPS2_GFX | BRF_GRA }, + { "c71s01.c9", 0x200000, 0x8507f41f, CPS2_GFX | BRF_GRA }, + { "c71s01.c10", 0x200000, 0x68b94ec0, CPS2_GFX | BRF_GRA }, + { "c71s01.c11", 0x200000, 0x8f7e8873, CPS2_GFX | BRF_GRA }, + { "c71s01.c12", 0x200000, 0x25f15b49, CPS2_GFX | BRF_GRA }, + + { "c71s01.m1", 0x020000, 0xb866c03c, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "vm3.02", 0x020000, 0xcc09faa1, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "c71s01.m3", 0x040000, 0xc95152a3, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + + { "vm3.11m", 0x400000, 0xe80e956e, CPS2_QSND | BRF_SND }, + { "vm3.12m", 0x400000, 0x9cd71557, CPS2_QSND | BRF_SND }, + { "c71s01.q3", 0x800000, 0xfd3a78ec, CPS2_QSND | BRF_SND }, + + { "vsavj.key", 0x000014, 0x36d28ab8, CPS2_ENCRYPTION_KEY }, +}; + +STD_ROM_PICK(Vsavs01) +STD_ROM_FN(Vsavs01) + +struct BurnDriver BurnDrvCpsVsavs01 = { + "vsavs01", "vsav", NULL, NULL, "2026", + "Vampire Savior Arrange (Hack, English RC3)\0", NULL, "Strygo", "CPS2", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_DSTLK, + NULL, Vsavs01RomInfo, Vsavs01RomName, NULL, NULL, NULL, NULL, Cps2CustomInputInfo, NULL, + VampireArrangeInit, Cps2PlusExit, Cps2Frame, CpsRedraw, Cps2PlusScan, + &CpsRecalcPal, 0x1000, 384, 224, 4, 3 +}; + +// Vampire Savior Arrange (Hack, Japan RC3) + +static struct BurnRomInfo Vsavs02RomDesc[] = { + { "c71s02.p1", 0x080000, 0x91599c43, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s02.p2", 0x080000, 0x989cde13, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s02.p3", 0x080000, 0xf0dbcf28, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "vm3j.06b", 0x080000, 0x2c4297e0, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s02.p5", 0x080000, 0x853d837e, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "vm3j.08a", 0x080000, 0x5773e5c9, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s02.p7", 0x080000, 0x6428a025, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s02.p8", 0x080000, 0x29316daa, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s02.p9", 0x400000, 0xe2089282, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + + { "c71s02.c1", 0x400000, 0x2eb67fcc, CPS2_GFX | BRF_GRA }, + { "c71s02.c2", 0x400000, 0xaee349f0, CPS2_GFX | BRF_GRA }, + { "c71s02.c3", 0x400000, 0x0e345bb8, CPS2_GFX | BRF_GRA }, + { "c71s02.c4", 0x400000, 0x33faf930, CPS2_GFX | BRF_GRA }, + { "vm3.14m", 0x400000, 0xc1a28e6c, CPS2_GFX | BRF_GRA }, + { "vm3.16m", 0x400000, 0x194a7304, CPS2_GFX | BRF_GRA }, + { "vm3.18m", 0x400000, 0xdf9a9f47, CPS2_GFX | BRF_GRA }, + { "vm3.20m", 0x400000, 0xc22fc3d9, CPS2_GFX | BRF_GRA }, + { "c71s02.c9", 0x200000, 0x8507f41f, CPS2_GFX | BRF_GRA }, + { "c71s02.c10", 0x200000, 0x68b94ec0, CPS2_GFX | BRF_GRA }, + { "c71s02.c11", 0x200000, 0x8f7e8873, CPS2_GFX | BRF_GRA }, + { "c71s02.c12", 0x200000, 0x25f15b49, CPS2_GFX | BRF_GRA }, + + { "c71s02.m1", 0x020000, 0xb866c03c, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "vm3.02", 0x020000, 0xcc09faa1, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "c71s02.m3", 0x040000, 0xc95152a3, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + + { "vm3.11m", 0x400000, 0xe80e956e, CPS2_QSND | BRF_SND }, + { "vm3.12m", 0x400000, 0x9cd71557, CPS2_QSND | BRF_SND }, + { "c71s02.q3", 0x800000, 0xfd3a78ec, CPS2_QSND | BRF_SND }, + + { "vsavj.key", 0x000014, 0x36d28ab8, CPS2_ENCRYPTION_KEY }, +}; + +STD_ROM_PICK(Vsavs02) +STD_ROM_FN(Vsavs02) + +struct BurnDriver BurnDrvCpsVsavs02 = { + "vsavs02", "vsav", NULL, NULL, "2026", + "Vampire Savior Arrange (Hack, Japan RC3)\0", NULL, "Strygo", "CPS2", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_DSTLK, + NULL, Vsavs02RomInfo, Vsavs02RomName, NULL, NULL, NULL, NULL, Cps2CustomInputInfo, NULL, + VampireArrangeInit, Cps2PlusExit, Cps2Frame, CpsRedraw, Cps2PlusScan, + &CpsRecalcPal, 0x1000, 384, 224, 4, 3 +}; + +// Vampire Savior 2 Arrange (Hack, English RC3) + +static struct BurnRomInfo Vsav2s05RomDesc[] = { + { "c76s05.p1", 0x080000, 0xeeefba15, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c76s05.p2", 0x080000, 0xe39be4dc, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c76s05.p3", 0x080000, 0x5fbed75f, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c76s05.p4", 0x080000, 0x253b76ef, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "vs2j.07", 0x080000, 0x8f885809, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c76s05.p6", 0x080000, 0x4e1b8eae, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "vs2j.09", 0x080000, 0xfac3c217, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c76s05.p8", 0x080000, 0x1e8a3df5, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c76s05.p9", 0x400000, 0xd524fdaa, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + + { "c76s05.c1", 0x400000, 0xb0c5665b, CPS2_GFX | BRF_GRA }, + { "c76s05.c2", 0x400000, 0xc5b6049f, CPS2_GFX | BRF_GRA }, + { "c76s05.c3", 0x400000, 0xa6448812, CPS2_GFX | BRF_GRA }, + { "c76s05.c4", 0x400000, 0x84067f87, CPS2_GFX | BRF_GRA }, + { "vs2.14m", 0x400000, 0xcd09bd63, CPS2_GFX | BRF_GRA }, + { "vs2.16m", 0x400000, 0xe0182c15, CPS2_GFX | BRF_GRA }, + { "vs2.18m", 0x400000, 0x778dc4f6, CPS2_GFX | BRF_GRA }, + { "vs2.20m", 0x400000, 0x605d9d1d, CPS2_GFX | BRF_GRA }, + { "c76s05.c9", 0x400000, 0xa45e1119, CPS2_GFX | BRF_GRA }, + { "c76s05.c10", 0x400000, 0x8b6832d1, CPS2_GFX | BRF_GRA }, + { "c76s05.c11", 0x400000, 0x58cacc2f, CPS2_GFX | BRF_GRA }, + { "c76s05.c12", 0x400000, 0x3d1408fe, CPS2_GFX | BRF_GRA }, + { "c76s05.c13", 0x400000, 0x7d5b6975, CPS2_GFX | BRF_GRA }, + { "c76s05.c14", 0x400000, 0x7d5b6975, CPS2_GFX | BRF_GRA }, + { "c76s05.c15", 0x400000, 0x7d5b6975, CPS2_GFX | BRF_GRA }, + { "c76s05.c16", 0x400000, 0x7d5b6975, CPS2_GFX | BRF_GRA }, + + { "c76s05.m1", 0x020000, 0x880f16d1, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "vs2.02", 0x020000, 0xc32dba09, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + + { "vs2.11m", 0x400000, 0xd67e47b7, CPS2_QSND | BRF_SND }, + { "vs2.12m", 0x400000, 0x6d020a14, CPS2_QSND | BRF_SND }, + { "c76s05.q3", 0x800000, 0xeb50211e, CPS2_QSND | BRF_SND }, + + { "vsav2.key", 0x000014, 0x289028ce, CPS2_ENCRYPTION_KEY }, +}; + +STD_ROM_PICK(Vsav2s05) +STD_ROM_FN(Vsav2s05) + +struct BurnDriver BurnDrvCpsVsav2s05 = { + "vsav2s05", "vsav2", NULL, NULL, "2026", + "Vampire Savior 2 Arrange (Hack, English RC3)\0", NULL, "Strygo", "CPS2", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_DSTLK, + NULL, Vsav2s05RomInfo, Vsav2s05RomName, NULL, NULL, NULL, NULL, Cps2CustomInputInfo, NULL, + VampireArrangeInit, Cps2PlusExit, Cps2Frame, CpsRedraw, Cps2PlusScan, + &CpsRecalcPal, 0x1000, 384, 224, 4, 3 +}; + +// Vampire Savior 2 Arrange (Hack, Japan RC3) + +static struct BurnRomInfo Vsav2s06RomDesc[] = { + { "c76s06.p1", 0x080000, 0x300870ae, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c76s06.p2", 0x080000, 0xcfe4a7b2, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c76s06.p3", 0x080000, 0x5fbed75f, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c76s06.p4", 0x080000, 0x253b76ef, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "vs2j.07", 0x080000, 0x8f885809, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c76s06.p6", 0x080000, 0x7bc795c2, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "vs2j.09", 0x080000, 0xfac3c217, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c76s06.p8", 0x080000, 0x1e8a3df5, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c76s06.p9", 0x400000, 0x1421c60b, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + + { "c76s06.c1", 0x400000, 0xb7751dee, CPS2_GFX | BRF_GRA }, + { "c76s06.c2", 0x400000, 0xa6dcea63, CPS2_GFX | BRF_GRA }, + { "c76s06.c3", 0x400000, 0x968eeeb0, CPS2_GFX | BRF_GRA }, + { "c76s06.c4", 0x400000, 0x591944a1, CPS2_GFX | BRF_GRA }, + { "vs2.14m", 0x400000, 0xcd09bd63, CPS2_GFX | BRF_GRA }, + { "vs2.16m", 0x400000, 0xe0182c15, CPS2_GFX | BRF_GRA }, + { "vs2.18m", 0x400000, 0x778dc4f6, CPS2_GFX | BRF_GRA }, + { "vs2.20m", 0x400000, 0x605d9d1d, CPS2_GFX | BRF_GRA }, + { "c76s06.c9", 0x400000, 0xa45e1119, CPS2_GFX | BRF_GRA }, + { "c76s06.c10", 0x400000, 0x8b6832d1, CPS2_GFX | BRF_GRA }, + { "c76s06.c11", 0x400000, 0x58cacc2f, CPS2_GFX | BRF_GRA }, + { "c76s06.c12", 0x400000, 0x3d1408fe, CPS2_GFX | BRF_GRA }, + { "c76s06.c13", 0x400000, 0x7d5b6975, CPS2_GFX | BRF_GRA }, + { "c76s06.c14", 0x400000, 0x7d5b6975, CPS2_GFX | BRF_GRA }, + { "c76s06.c15", 0x400000, 0x7d5b6975, CPS2_GFX | BRF_GRA }, + { "c76s06.c16", 0x400000, 0x7d5b6975, CPS2_GFX | BRF_GRA }, + + { "c76s06.m1", 0x020000, 0x880f16d1, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "vs2.02", 0x020000, 0xc32dba09, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + + { "vs2.11m", 0x400000, 0xd67e47b7, CPS2_QSND | BRF_SND }, + { "vs2.12m", 0x400000, 0x6d020a14, CPS2_QSND | BRF_SND }, + { "c76s06.q3", 0x800000, 0xeb50211e, CPS2_QSND | BRF_SND }, + + { "vsav2.key", 0x000014, 0x289028ce, CPS2_ENCRYPTION_KEY }, +}; + +STD_ROM_PICK(Vsav2s06) +STD_ROM_FN(Vsav2s06) + +struct BurnDriver BurnDrvCpsVsav2s06 = { + "vsav2s06", "vsav2", NULL, NULL, "2026", + "Vampire Savior 2 Arrange (Hack, Japan RC3)\0", NULL, "Strygo", "CPS2", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_DSTLK, + NULL, Vsav2s06RomInfo, Vsav2s06RomName, NULL, NULL, NULL, NULL, Cps2CustomInputInfo, NULL, + VampireArrangeInit, Cps2PlusExit, Cps2Frame, CpsRedraw, Cps2PlusScan, + &CpsRecalcPal, 0x1000, 384, 224, 4, 3 +}; + +// Night Warriors 2 Arrange (Hack, English RC3) + +static struct BurnRomInfo Vhunt2s02RomDesc[] = { + { "c75s02.p1", 0x080000, 0x9354e632, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c75s02.p2", 0x080000, 0x7f10f35e, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c75s02.p3", 0x080000, 0x87edf306, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c75s02.p4", 0x080000, 0x94118a84, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c75s02.p5", 0x080000, 0xa324ffeb, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c75s02.p6", 0x080000, 0x17b37f5a, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c75s02.p7", 0x080000, 0xf3159a3a, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c75s02.p8", 0x080000, 0xecd2d939, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c75s02.p9", 0x400000, 0x673d43b1, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + + { "c75s02.c1", 0x400000, 0x82e825c6, CPS2_GFX | BRF_GRA }, + { "c75s02.c2", 0x400000, 0x678feab2, CPS2_GFX | BRF_GRA }, + { "c75s02.c3", 0x400000, 0xb9e1cbf8, CPS2_GFX | BRF_GRA }, + { "c75s02.c4", 0x400000, 0x68d603b4, CPS2_GFX | BRF_GRA }, + { "vh2.14m", 0x400000, 0xcd09bd63, CPS2_GFX | BRF_GRA }, + { "vh2.16m", 0x400000, 0xe0182c15, CPS2_GFX | BRF_GRA }, + { "vh2.18m", 0x400000, 0x778dc4f6, CPS2_GFX | BRF_GRA }, + { "vh2.20m", 0x400000, 0x605d9d1d, CPS2_GFX | BRF_GRA }, + { "c75s02.c9", 0x200000, 0xd7d4fecc, CPS2_GFX | BRF_GRA }, + { "c75s02.c10", 0x200000, 0x95c0d576, CPS2_GFX | BRF_GRA }, + { "c75s02.c11", 0x200000, 0x66a27497, CPS2_GFX | BRF_GRA }, + { "c75s02.c12", 0x200000, 0x958494ec, CPS2_GFX | BRF_GRA }, + + { "c75s02.m1", 0x020000, 0xf6088d87, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "c75s02.m2", 0x020000, 0x7c4fae46, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "c75s02.m3", 0x040000, 0x04ef903d, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + + { "vh2.11m", 0x400000, 0x38922efd, CPS2_QSND | BRF_SND }, + { "vh2.12m", 0x400000, 0x6e2430af, CPS2_QSND | BRF_SND }, + { "c75s02.q3", 0x800000, 0x24813909, CPS2_QSND | BRF_SND }, + + { "vhunt2.key", 0x000014, 0x61306b20, CPS2_ENCRYPTION_KEY }, +}; + +STD_ROM_PICK(Vhunt2s02) +STD_ROM_FN(Vhunt2s02) + +struct BurnDriver BurnDrvCpsVhunt2s02 = { + "vhunt2s02", "vhunt2", NULL, NULL, "2026", + "Night Warriors 2 Arrange (Hack, English RC3)\0", NULL, "Strygo", "CPS2", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_DSTLK, + NULL, Vhunt2s02RomInfo, Vhunt2s02RomName, NULL, NULL, NULL, NULL, Cps2CustomInputInfo, NULL, + VampireArrangeInit, Cps2PlusExit, Cps2Frame, CpsRedraw, Cps2PlusScan, + &CpsRecalcPal, 0x1000, 384, 224, 4, 3 +}; + +// Vampire Hunter 2 Arrange (Hack, Japan RC3) + +static struct BurnRomInfo Vhunt2s03RomDesc[] = { + { "c75s03.p1", 0x080000, 0x7797e2a4, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c75s03.p2", 0x080000, 0xc0703475, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c75s03.p3", 0x080000, 0x87edf306, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c75s03.p4", 0x080000, 0x94118a84, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c75s03.p5", 0x080000, 0xa324ffeb, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c75s03.p6", 0x080000, 0x79dbd15c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c75s03.p7", 0x080000, 0xf3159a3a, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c75s03.p8", 0x080000, 0xecd2d939, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c75s03.p9", 0x400000, 0x2d91d6a5, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + + { "c75s03.c1", 0x400000, 0x6a3d7b6a, CPS2_GFX | BRF_GRA }, + { "c75s03.c2", 0x400000, 0xebcb10da, CPS2_GFX | BRF_GRA }, + { "c75s03.c3", 0x400000, 0xda8cfcd4, CPS2_GFX | BRF_GRA }, + { "c75s03.c4", 0x400000, 0x108fd9e0, CPS2_GFX | BRF_GRA }, + { "vh2.14m", 0x400000, 0xcd09bd63, CPS2_GFX | BRF_GRA }, + { "vh2.16m", 0x400000, 0xe0182c15, CPS2_GFX | BRF_GRA }, + { "vh2.18m", 0x400000, 0x778dc4f6, CPS2_GFX | BRF_GRA }, + { "vh2.20m", 0x400000, 0x605d9d1d, CPS2_GFX | BRF_GRA }, + { "c75s03.c9", 0x200000, 0x069fd6cc, CPS2_GFX | BRF_GRA }, + { "c75s03.c10", 0x200000, 0xdfde0582, CPS2_GFX | BRF_GRA }, + { "c75s03.c11", 0x200000, 0x2438f71b, CPS2_GFX | BRF_GRA }, + { "c75s03.c12", 0x200000, 0xdf587c66, CPS2_GFX | BRF_GRA }, + + { "c75s03.m1", 0x020000, 0xf6088d87, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "c75s03.m2", 0x020000, 0x7c4fae46, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "c75s03.m3", 0x040000, 0x04ef903d, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + + { "vh2.11m", 0x400000, 0x38922efd, CPS2_QSND | BRF_SND }, + { "vh2.12m", 0x400000, 0x6e2430af, CPS2_QSND | BRF_SND }, + { "c75s03.q3", 0x800000, 0x24813909, CPS2_QSND | BRF_SND }, + + { "vhunt2.key", 0x000014, 0x61306b20, CPS2_ENCRYPTION_KEY }, +}; + +STD_ROM_PICK(Vhunt2s03) +STD_ROM_FN(Vhunt2s03) + +struct BurnDriver BurnDrvCpsVhunt2s03 = { + "vhunt2s03", "vhunt2", NULL, NULL, "2026", + "Vampire Hunter 2 Arrange (Hack, Japan RC3)\0", NULL, "Strygo", "CPS2", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_DSTLK, + NULL, Vhunt2s03RomInfo, Vhunt2s03RomName, NULL, NULL, NULL, NULL, Cps2CustomInputInfo, NULL, + VampireArrangeInit, Cps2PlusExit, Cps2Frame, CpsRedraw, Cps2PlusScan, + &CpsRecalcPal, 0x1000, 384, 224, 4, 3 +}; diff --git a/src/burn/drv/capcom/meson.build b/src/burn/drv/capcom/meson.build index c2e3f23e9..56b7b71c6 100644 --- a/src/burn/drv/capcom/meson.build +++ b/src/burn/drv/capcom/meson.build @@ -33,5 +33,6 @@ group_srcs = [ drivers = { 'd_cps1.cpp': ['ay8910', 'eeprom', 'm68000', 'msm5205', 'msm6295', 'samples', 'timekpr', 'ym2151', 'ym2203', 'z80'], 'd_cps2.cpp': ['ay8910', 'eeprom', 'm68000', 'msm5205', 'msm6295', 'samples', 'ym2151', 'ym2203', 'z80'], + 'd_cps2_custom.cpp': ['ay8910', 'eeprom', 'm68000', 'msm5205', 'msm6295', 'samples', 'ym2151', 'ym2203', 'z80'], 'd_mitchell.cpp': ['ay8910', 'eeprom', 'm68000', 'msm5205', 'msm6295', 'samples', 'ym2151', 'ym2203', 'ym2413', 'z80'], } diff --git a/src/burn/drv/capcom/qs.cpp b/src/burn/drv/capcom/qs.cpp index 858caea83..7a0b5812d 100644 --- a/src/burn/drv/capcom/qs.cpp +++ b/src/burn/drv/capcom/qs.cpp @@ -21,18 +21,9 @@ INT32 QsndInit() } BurnTimerInit(qsndTimerOver, NULL); - if (Cps1Qs == 1) { - nCpsZ80Cycles = 8000000 * 100 / nBurnFPS; - BurnTimerAttachZet(8000000); - } else { - if (Cps2Turbo) { - nCpsZ80Cycles = 16000000 * 100 / nBurnFPS; - BurnTimerAttachZet(16000000); - } else { - nCpsZ80Cycles = 8000000 * 100 / nBurnFPS; - BurnTimerAttachZet(8000000); - } - } + INT32 nClock = (Cps1Qs == 1) ? 8000000 : nQsndZ80Clock; + nCpsZ80Cycles = nClock * 100 / nBurnFPS; + BurnTimerAttachZet(nClock); if (nBurnSoundRate >= 0) { nRate = nBurnSoundRate; @@ -66,11 +57,11 @@ void QsndExit() QsndZExit(); } -INT32 QsndScan(INT32 nAction) +INT32 QsndScan(INT32 nAction, INT32 bScanHistory) { if (nAction & ACB_DRIVER_DATA) { QsndZScan(nAction); // Scan Z80 - QscScan(nAction); // Scan QSound Chip + QscScan(nAction, bScanHistory); // Scan QSound Chip BurnTimerScan(nAction, NULL); SCAN_VAR(nQsndCyclesExtra); @@ -93,9 +84,6 @@ void QsndEndFrame() { BurnTimerEndFrame(nCpsZ80Cycles); if (pBurnSoundOut) QscUpdate(nBurnSoundLen); - if (Cps2Turbo && pBurnSoundOut) { - BurnSampleRender(pBurnSoundOut, nBurnSoundLen); - } nQsndCyclesExtra = ZetTotalCycles() - nCpsZ80Cycles; ZetClose(); diff --git a/src/burn/drv/capcom/qs_c.cpp b/src/burn/drv/capcom/qs_c.cpp index e4cd0c35b..79873cc5d 100644 --- a/src/burn/drv/capcom/qs_c.cpp +++ b/src/burn/drv/capcom/qs_c.cpp @@ -310,7 +310,7 @@ inline INT16 get_sample(UINT16 bank, UINT16 address) UINT8 sample_data; if(nCpsQSamLen) - rom_mask = nCpsQSamLen - 1; + rom_mask = nCpsQSamMask ? nCpsQSamMask : nCpsQSamLen - 1; if (! rom_mask) return 0; // no ROM loaded @@ -765,7 +765,7 @@ void QscSetRoute(INT32 nIndex, double nVolume, INT32 nRouteDir) QsndOutputDir[nIndex] = nRouteDir; } -INT32 QscScan(INT32 nAction) +INT32 QscScan(INT32 nAction, INT32 bScanHistory) { struct BurnArea ba; char szName[16]; @@ -778,8 +778,8 @@ INT32 QscScan(INT32 nAction) ba.szName = szName; BurnAcb(&ba); - if (CpsXmvSixEnabled) { - // xmvsf3v3: the resampler too, so its states replay exactly (other games' states keep their size) + if (bScanHistory) { + // Preserve resampler history when replaying a saved frame. SCAN_VAR(nDelta); SCAN_VAR(nPos); SCAN_VAR(interpolate_buffer); diff --git a/src/burn/drv/capcom/qs_z.cpp b/src/burn/drv/capcom/qs_z.cpp index 9e7590dd9..3506cdc03 100644 --- a/src/burn/drv/capcom/qs_z.cpp +++ b/src/burn/drv/capcom/qs_z.cpp @@ -1,6 +1,9 @@ #include "cps.h" // QSound - Z80 +INT32 nQsndZ80Clock = 8000000; +INT32 nQsndZBankOffset = 0x8000; + static INT32 nQsndZBank = 0; static UINT8 QscCmd[2] = {0, 0}; @@ -10,7 +13,7 @@ static INT32 QsndZBankMap() UINT32 nOff; UINT8* Bank; nOff = nQsndZBank << 14; - nOff += (Cps2Turbo) ? 0x10000 : 0x8000; // sfz3mix .22 z80 rom stored in mame memmap format (banks start at 0x10000, instead of 0x8000 like fbneo) + nOff += nQsndZBankOffset; if (Cps1Qs == 0) { if (nOff + 0x4000 > nCpsZRomLen) { // End of bank is out of range @@ -52,10 +55,7 @@ void __fastcall QsndZWrite(UINT16 a, UINT8 d) } if (a == 0xd003) { INT32 nNewBank = d & 0x0f; - if (nQsndZBank != nNewBank) { - nQsndZBank = nNewBank; - QsndZBankMap(); - } + QsndZSetBank(nNewBank); } } @@ -119,8 +119,30 @@ INT32 QsndZInit() return 0; } +void QsndZSetBank(INT32 nBank) +{ + if (nQsndZBank != nBank) { + nQsndZBank = nBank; + QsndZBankMap(); + } +} + +INT32 QsndZGetBank() +{ + return nQsndZBank; +} + +void QsndZReset() +{ + nQsndZBank = 0; + QsndZBankMap(); + QscCmd[0] = QscCmd[1] = 0; +} + INT32 QsndZExit() { + nQsndZ80Clock = 8000000; + nQsndZBankOffset = 0x8000; ZetExit(); return 0; } diff --git a/src/burn/drv/capcom/xmvsf3v3.h b/src/burn/drv/capcom/xmvsf3v3.h index 4cbfbe984..04fafe266 100644 --- a/src/burn/drv/capcom/xmvsf3v3.h +++ b/src/burn/drv/capcom/xmvsf3v3.h @@ -7,7 +7,7 @@ // 0x810000-0x81ffff 64KB of extra work RAM, each 68000 word stored high byte first // The ROMs also mark the mini portraits on the HUD so that this board can draw them in the // fighter's chosen colour (one of eighteen), decoded from the colour table in the program ROM. -// Nothing here is used by any other game: cps_rw.cpp only enables it for the 3v3 drivers. +// Enabled by the 3v3 drivers in d_cps2_custom.cpp. #ifndef XMVSF3V3_H #define XMVSF3V3_H From aeecee6a50e374254ae21d98c8b4454df4a06fb6 Mon Sep 17 00:00:00 2001 From: MCa Date: Mon, 26 Oct 2026 22:03:07 -0300 Subject: [PATCH 09/26] re upping the owner drawn menu memory leak Fix (#2836) --- src/burner/win32/menu.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/burner/win32/menu.cpp b/src/burner/win32/menu.cpp index ff7257f75..5ad09ec1e 100644 --- a/src/burner/win32/menu.cpp +++ b/src/burner/win32/menu.cpp @@ -140,6 +140,36 @@ static INT32 GetCurrentMonitorHigh() { return monitorInfo.rcMonitor.bottom; } +//In order to properly clean up the currentMenuInfo structs created to handle owner Drawn components it is necessary to iterate over all the menus and clean +//the mii.dwItemData variable +void CleanUpMenuInfos(HMENU hMenu) +{ + MENUINFO mi = { sizeof(MENUINFO) }; + int count = GetMenuItemCount(hMenu); + for (int i = 0; i < count; i++) { + HMENU hSub = GetSubMenu(hMenu, i); + if(hSub) + { + CleanUpMenuInfos(hSub); + } + + MENUITEMINFO mii = { sizeof(MENUITEMINFO) }; + + mii.fMask = MIIM_FTYPE | MIIM_DATA; + if (!GetMenuItemInfo(hMenu, i, TRUE, &mii)) + continue; + + if ((mii.fType & MFT_OWNERDRAW) && mii.dwItemData != 0) + { + delete (CurrentItemInfo*)mii.dwItemData; + mii.fMask = MIIM_DATA; + mii.dwItemData = 0; + SetMenuItemInfo(hMenu, i, TRUE, &mii); + } + + } +} + void ApplyMenuBackground(HMENU hMenu, HBRUSH hbr) { MENUINFO mi = { sizeof(MENUINFO) }; @@ -438,6 +468,11 @@ void DisplayPopupMenu(int nMenu) if (!bModelessMenu) { UnhookWindowsHookEx(hMenuHook); } + + if (uiOwnerDrawActive) { + CleanUpMenuInfos(hPopupMenu); + } + } } From 8a575fd70d0ad6cdc2b60cc73a65e0de11838750 Mon Sep 17 00:00:00 2001 From: dinkc64 Date: Mon, 26 Oct 2026 21:14:43 -0400 Subject: [PATCH 10/26] use a higher resolution clock for calculating fps --- src/burner/win32/run.cpp | 58 ++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/src/burner/win32/run.cpp b/src/burner/win32/run.cpp index 60e2ab7ab..6cad4c09c 100644 --- a/src/burner/win32/run.cpp +++ b/src/burner/win32/run.cpp @@ -168,31 +168,59 @@ static int GetInput(bool bCopy) return 0; } -static time_t fpstimer; -static unsigned int nPreviousFrames; +static LARGE_INTEGER qpcFreq, qpcLast; +static UINT32 nLastEmu; static void DisplayFPSInit() { nDoFPS = 0; - fpstimer = 0; - nPreviousFrames = (bAppDoFast) ? nFramesEmulated : nFramesRendered; + QueryPerformanceFrequency(&qpcFreq); + QueryPerformanceCounter(&qpcLast); + nLastEmu = nFramesEmulated; } static void DisplayFPS() { - const int nFPSTotal = (bAppDoFast) ? nFramesEmulated : nFramesRendered; + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + double dt = (double)(now.QuadPart - qpcLast.QuadPart) / qpcFreq.QuadPart; + if (dt < 1.0) return; // update once per second - TCHAR fpsstring[20]; - time_t temptime = clock(); - double fps = (double)(nFPSTotal - nPreviousFrames) * CLOCKS_PER_SEC / (temptime - fpstimer); + double fps = (nFramesEmulated - nLastEmu) / dt; - _sntprintf(fpsstring, 7, _T("%2.2lf"), fps); - if (fpstimer && temptime - fpstimer>0) { // avoid strange fps values - VidSNewShortMsg(fpsstring, 0xDFDFFF, 480, 0); + TCHAR s[32]; + _sntprintf(s, 32, _T("%2.2lf"), fps); + VidSNewShortMsg(s, 0xDFDFFF, 480, 0); + + qpcLast = now; + nLastEmu = nFramesEmulated; +} + +static void DisplayFPSCheckState() +{ + static int bLastFast = -1, bLastPause = -1; + static LONGLONG nLastCall = 0; + static int timer = 0; + + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + + // frame normally runs every ~16.67ms (at 60hz) + // a gap over 100ms means something blocked the loop: menu, window drag, dialog, loading... + const bool bStalled = nLastCall && qpcFreq.QuadPart && + (now.QuadPart - nLastCall) > qpcFreq.QuadPart / 10; + nLastCall = now.QuadPart; + + if (bStalled || bAppDoFast != bLastFast || bRunPause != bLastPause) { + timer = 30; + bLastFast = bAppDoFast; + bLastPause = bRunPause; + } + if (timer > 0) { + // it takes a few frames to stabilize... + timer--; + DisplayFPSInit(); } - - fpstimer = temptime; - nPreviousFrames = nFPSTotal; } // define this function somewhere above RunMessageLoop() @@ -232,6 +260,8 @@ int RunFrame(int bDraw, int bPause) static int bPrevPause = 0; static int bPrevDraw = 0; + if (bShowFPS) DisplayFPSCheckState(); + if (bPrevDraw && !bPause) { VidPaint(0); // paint the screen (no need to validate) From 7aaf88922235d41873d799014e00c4da14773c3d Mon Sep 17 00:00:00 2001 From: KlaviculaAG <205328730+KlaviculaAG@users.noreply.github.com> Date: Mon, 26 Oct 2026 22:30:01 -0300 Subject: [PATCH 11/26] adj. in d_cps2.cpp - strygo hacks (#2837) * adj. in d_cps2.cpp - sfaex, ssf2tgmc strygo hacks - new Street Fighter Alpha EX (U/A/J) update Super Street Fighter II Turbo: Grand Master Challenge * d_cps2.cpp - adj. in ffex romsets strygo hacks - update Final Fight Ex (U/J/RE) --- src/burn/drv/capcom/d_cps2.cpp | 277 +++++++++++++++++++++++---------- 1 file changed, 191 insertions(+), 86 deletions(-) diff --git a/src/burn/drv/capcom/d_cps2.cpp b/src/burn/drv/capcom/d_cps2.cpp index 2c1703cc5..951d63717 100644 --- a/src/burn/drv/capcom/d_cps2.cpp +++ b/src/burn/drv/capcom/d_cps2.cpp @@ -16558,34 +16558,34 @@ struct BurnDriver BurnDrvCpsSsf2xdced = { // https://strygo.github.io/arcade-patches/ // ----------------------------------------------------------------------------- -// Final Fight EX (Hack, CPS2 hardware, USA RC1) - +// Final Fight EX (Hack, CPS2 hardware, USA RC2) +// 2026-10-25 static struct BurnRomInfo Ffex2uRomDesc[] = { - { "sz3ud.03c", 0x080000, 0xe022fd18, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3ud.04c", 0x080000, 0x9c228c18, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.05c", 0x080000, 0x787bb7f2, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.06c", 0x080000, 0x5652684a, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.07c", 0x080000, 0x6e430b5c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.08c", 0x080000, 0x0b37c61a, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.09c", 0x080000, 0xc0a36b4d, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.10b", 0x080000, 0x85f1c748, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffexd.03c", 0x080000, 0x4f9873c4, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffexd.04c", 0x080000, 0x4957978c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffex.05c", 0x080000, 0x787bb7f2, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffex.06c", 0x080000, 0x5652684a, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffex.07c", 0x080000, 0x6e430b5c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffex.08c", 0x080000, 0x0b37c61a, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffex.09c", 0x080000, 0xc0a36b4d, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffex.10b", 0x080000, 0x85f1c748, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.13m", 0x400000, 0x48cbc129, CPS2_GFX | BRF_GRA }, - { "sz3.15m", 0x400000, 0xfd20f3aa, CPS2_GFX | BRF_GRA }, - { "sz3.17m", 0x400000, 0x5ea87831, CPS2_GFX | BRF_GRA }, - { "sz3.19m", 0x400000, 0xda6a3e7e, CPS2_GFX | BRF_GRA }, - { "sz3.14m", 0x400000, 0xeca6de34, CPS2_GFX | BRF_GRA }, - { "sz3.16m", 0x400000, 0x17069cbd, CPS2_GFX | BRF_GRA }, - { "sz3.18m", 0x400000, 0x0f560d8c, CPS2_GFX | BRF_GRA }, - { "sz3.20m", 0x400000, 0xe96d3923, CPS2_GFX | BRF_GRA }, + { "ffex.13m", 0x400000, 0x48cbc129, CPS2_GFX | BRF_GRA }, + { "ffex.15m", 0x400000, 0xfd20f3aa, CPS2_GFX | BRF_GRA }, + { "ffex.17m", 0x400000, 0x5ea87831, CPS2_GFX | BRF_GRA }, + { "ffex.19m", 0x400000, 0xda6a3e7e, CPS2_GFX | BRF_GRA }, + { "ffex.14m", 0x400000, 0xeca6de34, CPS2_GFX | BRF_GRA }, + { "ffex.16m", 0x400000, 0x17069cbd, CPS2_GFX | BRF_GRA }, + { "ffex.18m", 0x400000, 0x0f560d8c, CPS2_GFX | BRF_GRA }, + { "ffex.20m", 0x400000, 0xe96d3923, CPS2_GFX | BRF_GRA }, - { "sz3.01", 0x020000, 0x03d74a70, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - { "sz3.02", 0x020000, 0x7ee8cdcd, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "ffex.01", 0x020000, 0x03d74a70, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "ffex.02", 0x020000, 0x7ee8cdcd, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - { "sz3.11m", 0x400000, 0xbea96b44, CPS2_QSND | BRF_SND }, - { "sz3.12m", 0x400000, 0xdf5e34c3, CPS2_QSND | BRF_SND }, + { "ffex.11m", 0x400000, 0xbea96b44, CPS2_QSND | BRF_SND }, + { "ffex.12m", 0x400000, 0xdf5e34c3, CPS2_QSND | BRF_SND }, - { "phoenix.key", 0x000014, 0x2cf772b0, CPS2_ENCRYPTION_KEY }, + { "phoenix.key", 0x000014, 0x2cf772b0, CPS2_ENCRYPTION_KEY }, }; STD_ROM_PICK(Ffex2u) @@ -16593,7 +16593,7 @@ STD_ROM_FN(Ffex2u) struct BurnDriver BurnDrvCpsFfex2u = { "ffex2u", NULL, NULL, NULL, "2026", - "Final Fight EX (Hack, CPS2 hardware, USA RC1)\0", NULL, "Strygo", "CPS2", + "Final Fight EX (Hack, CPS2 hardware, USA RC2)\0", NULL, "Strygo", "CPS2", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_HACK, 2, HARDWARE_CAPCOM_CPS2, GBF_SCRFIGHT, 0, NULL, Ffex2uRomInfo, Ffex2uRomName, NULL, NULL, NULL, NULL, Ffex2InputInfo, NULL, @@ -16601,34 +16601,34 @@ struct BurnDriver BurnDrvCpsFfex2u = { &CpsRecalcPal, 0x1000, 384, 224, 4, 3 }; -// Final Fight EX (Hack, CPS2 hardware, Japan RC1) - +// Final Fight EX (Hack, CPS2 hardware, Japan RC2) +// 2026-10-25 static struct BurnRomInfo Ffex2jRomDesc[] = { - { "sz3ud.03c", 0x080000, 0x9eb92f51, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3ud.04c", 0x080000, 0x3a2393c0, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.05c", 0x080000, 0x787bb7f2, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.06c", 0x080000, 0xa8d2d10b, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.07c", 0x080000, 0xbcf7297e, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.08c", 0x080000, 0x7afbcbad, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.09c", 0x080000, 0x0642626b, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.10b", 0x080000, 0xb4208a79, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffexjd.03c", 0x080000, 0x789ff036, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffexjd.04c", 0x080000, 0x613c6ecc, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffex.05c", 0x080000, 0x787bb7f2, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffexj.06c", 0x080000, 0xa8d2d10b, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffexj.07c", 0x080000, 0xbcf7297e, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffexj.08c", 0x080000, 0x7afbcbad, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffexj.09c", 0x080000, 0x0642626b, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffexj.10b", 0x080000, 0xb4208a79, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.13m", 0x400000, 0xd24130e3, CPS2_GFX | BRF_GRA }, - { "sz3.15m", 0x400000, 0xcef0a6b3, CPS2_GFX | BRF_GRA }, - { "sz3.17m", 0x400000, 0x00464598, CPS2_GFX | BRF_GRA }, - { "sz3.19m", 0x400000, 0x6f5379cc, CPS2_GFX | BRF_GRA }, - { "sz3.14m", 0x400000, 0xae054c04, CPS2_GFX | BRF_GRA }, - { "sz3.16m", 0x400000, 0x0e09c2ba, CPS2_GFX | BRF_GRA }, - { "sz3.18m", 0x400000, 0x0f560d8c, CPS2_GFX | BRF_GRA }, - { "sz3.20m", 0x400000, 0xe96d3923, CPS2_GFX | BRF_GRA }, + { "ffexj.13m", 0x400000, 0xd24130e3, CPS2_GFX | BRF_GRA }, + { "ffexj.15m", 0x400000, 0xcef0a6b3, CPS2_GFX | BRF_GRA }, + { "ffexj.17m", 0x400000, 0x00464598, CPS2_GFX | BRF_GRA }, + { "ffexj.19m", 0x400000, 0x6f5379cc, CPS2_GFX | BRF_GRA }, + { "ffexj.14m", 0x400000, 0xae054c04, CPS2_GFX | BRF_GRA }, + { "ffexj.16m", 0x400000, 0x0e09c2ba, CPS2_GFX | BRF_GRA }, + { "ffex.18m", 0x400000, 0x0f560d8c, CPS2_GFX | BRF_GRA }, + { "ffex.20m", 0x400000, 0xe96d3923, CPS2_GFX | BRF_GRA }, - { "sz3.01", 0x020000, 0x36ef522c, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - { "sz3.02", 0x020000, 0x7ee8cdcd, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "ffexj.01", 0x020000, 0x36ef522c, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "ffex.02", 0x020000, 0x7ee8cdcd, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - { "sz3.11m", 0x400000, 0xbea96b44, CPS2_QSND | BRF_SND }, - { "sz3.12m", 0x400000, 0xdf5e34c3, CPS2_QSND | BRF_SND }, + { "ffex.11m", 0x400000, 0xbea96b44, CPS2_QSND | BRF_SND }, + { "ffex.12m", 0x400000, 0xdf5e34c3, CPS2_QSND | BRF_SND }, - { "phoenix.key", 0x000014, 0x2cf772b0, CPS2_ENCRYPTION_KEY }, + { "phoenix.key", 0x000014, 0x2cf772b0, CPS2_ENCRYPTION_KEY }, }; STD_ROM_PICK(Ffex2j) @@ -16636,7 +16636,7 @@ STD_ROM_FN(Ffex2j) struct BurnDriver BurnDrvCpsFfex2j = { "ffex2j", "ffex2u", NULL, NULL, "2026", - "Final Fight EX (Hack, CPS2 hardware, Japan RC1)\0", NULL, "Strygo", "CPS2", + "Final Fight EX (Hack, CPS2 hardware, Japan RC2)\0", NULL, "Strygo", "CPS2", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS2, GBF_SCRFIGHT, 0, NULL, Ffex2jRomInfo, Ffex2jRomName, NULL, NULL, NULL, NULL, Ffex2InputInfo, NULL, @@ -16644,34 +16644,34 @@ struct BurnDriver BurnDrvCpsFfex2j = { &CpsRecalcPal, 0x1000, 384, 224, 4, 3 }; -// Final Fight EX (Hack, CPS2 hardware, English Restoration RC1) - +// Final Fight EX (Hack, CPS2 hardware, English Restoration RC2) +// 2026-10-25 static struct BurnRomInfo Ffex2rRomDesc[] = { - { "sz3ud.03c", 0x080000, 0xd1129b95, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3ud.04c", 0x080000, 0x5d10506d, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.05c", 0x080000, 0x787bb7f2, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.06c", 0x080000, 0x5652684a, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.07c", 0x080000, 0x6e430b5c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.08c", 0x080000, 0x64f42ba9, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.09c", 0x080000, 0xbd69566a, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.10b", 0x080000, 0x9539a4aa, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffexrd.03c", 0x080000, 0xbce36dc0, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffexrd.04c", 0x080000, 0x273ae5d9, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffex.05c", 0x080000, 0x787bb7f2, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffex.06c", 0x080000, 0x5652684a, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffex.07c", 0x080000, 0x6e430b5c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffexr.08c", 0x080000, 0x64f42ba9, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffexr.09c", 0x080000, 0xbd69566a, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "ffexr.10b", 0x080000, 0x9539a4aa, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sz3.13m", 0x400000, 0x231f811f, CPS2_GFX | BRF_GRA }, - { "sz3.15m", 0x400000, 0x26cea23a, CPS2_GFX | BRF_GRA }, - { "sz3.17m", 0x400000, 0xf118f464, CPS2_GFX | BRF_GRA }, - { "sz3.19m", 0x400000, 0x876d7d45, CPS2_GFX | BRF_GRA }, - { "sz3.14m", 0x400000, 0xeca6de34, CPS2_GFX | BRF_GRA }, - { "sz3.16m", 0x400000, 0x17069cbd, CPS2_GFX | BRF_GRA }, - { "sz3.18m", 0x400000, 0x0f560d8c, CPS2_GFX | BRF_GRA }, - { "sz3.20m", 0x400000, 0xe96d3923, CPS2_GFX | BRF_GRA }, + { "ffexr.13m", 0x400000, 0x231f811f, CPS2_GFX | BRF_GRA }, + { "ffexr.15m", 0x400000, 0x26cea23a, CPS2_GFX | BRF_GRA }, + { "ffexr.17m", 0x400000, 0xf118f464, CPS2_GFX | BRF_GRA }, + { "ffexr.19m", 0x400000, 0x876d7d45, CPS2_GFX | BRF_GRA }, + { "ffex.14m", 0x400000, 0xeca6de34, CPS2_GFX | BRF_GRA }, + { "ffex.16m", 0x400000, 0x17069cbd, CPS2_GFX | BRF_GRA }, + { "ffex.18m", 0x400000, 0x0f560d8c, CPS2_GFX | BRF_GRA }, + { "ffex.20m", 0x400000, 0xe96d3923, CPS2_GFX | BRF_GRA }, - { "sz3.01", 0x020000, 0x3c1c4965, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - { "sz3.02", 0x020000, 0x7ee8cdcd, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "ffexr.01", 0x020000, 0x3c1c4965, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "ffex.02", 0x020000, 0x7ee8cdcd, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - { "sz3.11m", 0x400000, 0xbea96b44, CPS2_QSND | BRF_SND }, - { "sz3.12m", 0x400000, 0xdf5e34c3, CPS2_QSND | BRF_SND }, + { "ffex.11m", 0x400000, 0xbea96b44, CPS2_QSND | BRF_SND }, + { "ffex.12m", 0x400000, 0xdf5e34c3, CPS2_QSND | BRF_SND }, - { "phoenix.key", 0x000014, 0x2cf772b0, CPS2_ENCRYPTION_KEY }, + { "phoenix.key", 0x000014, 0x2cf772b0, CPS2_ENCRYPTION_KEY }, }; STD_ROM_PICK(Ffex2r) @@ -16679,7 +16679,7 @@ STD_ROM_FN(Ffex2r) struct BurnDriver BurnDrvCpsFfex2r = { "ffex2r", "ffex2u", NULL, NULL, "2026", - "Final Fight EX (Hack, CPS2 hardware, English Restoration RC1)\0", NULL, "Strygo", "CPS2", + "Final Fight EX (Hack, CPS2 hardware, English Restoration RC2)\0", NULL, "Strygo", "CPS2", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS2, GBF_SCRFIGHT, 0, NULL, Ffex2rRomInfo, Ffex2rRomName, NULL, NULL, NULL, NULL, Ffex2InputInfo, NULL, @@ -17050,6 +17050,111 @@ struct BurnDriver BurnDrvCpsSfzrej = { &CpsRecalcPal, 0x1000, 384, 224, 4, 3 }; +// Street Fighter Alpha EX: Warriors' Dreams (Hack, USA RC1) +// 2026-10-26 +static struct BurnRomInfo SfaexRomDesc[] = { + { "sfzex.03c", 0x080000, 0xa0c070cb, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "sfzre.04b", 0x080000, 0x8d9b2480, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "sfzre.05a", 0x080000, 0x8cf0a98f, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "sfzex.06", 0x080000, 0x9b5fc006, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + + { "sfzex.14m", 0x200000, 0x5e90868c, CPS2_GFX | BRF_GRA }, + { "sfzex.16m", 0x200000, 0x97ac2170, CPS2_GFX | BRF_GRA }, + { "sfzex.18m", 0x200000, 0x5e414b4f, CPS2_GFX | BRF_GRA }, + { "sfzex.20m", 0x200000, 0xf29757b8, CPS2_GFX | BRF_GRA }, + + { "sfzex.01", 0x020000, 0xe52887c9, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "sfz.02", 0x020000, 0x45f46a08, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + + { "sfz.11m", 0x200000, 0xc4b093cd, CPS2_QSND | BRF_SND }, + { "sfz.12m", 0x200000, 0x8bdbc4b4, CPS2_QSND | BRF_SND }, + + { "sfzj.key", 0x000014, 0x355d85b8, CPS2_ENCRYPTION_KEY }, +}; + +STD_ROM_PICK(Sfaex) +STD_ROM_FN(Sfaex) + +struct BurnDriver BurnDrvCpsSfaex = { + "sfaex", "sfa", NULL, NULL, "2026", + "Street Fighter Alpha EX: Warriors' Dreams (Hack, USA RC1)\0", NULL, "Strygo", "CPS2", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK | BDF_HISCORE_SUPPORTED, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_SF, + NULL, SfaexRomInfo, SfaexRomName, NULL, NULL, NULL, NULL, Cps2FightingInputInfo, NULL, + Cps2Init, DrvExit, Cps2Frame, CpsRedraw, CpsAreaScan, + &CpsRecalcPal, 0x1000, 384, 224, 4, 3 +}; + +// Street Fighter Zero EX (Hack, Asia RC1) +// 2026-10-26 +static struct BurnRomInfo SfzexaRomDesc[] = { + { "sfzexa.03c", 0x080000, 0x14c3e640, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "sfzre.04b", 0x080000, 0x8d9b2480, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "sfzre.05a", 0x080000, 0x8cf0a98f, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "sfzexa.06", 0x080000, 0x4185da04, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + + { "sfzexa.14m", 0x200000, 0x31318783, CPS2_GFX | BRF_GRA }, + { "sfzexa.16m", 0x200000, 0xb905c57d, CPS2_GFX | BRF_GRA }, + { "sfzexa.18m", 0x200000, 0x0ed47cd2, CPS2_GFX | BRF_GRA }, + { "sfzexa.20m", 0x200000, 0x688f9434, CPS2_GFX | BRF_GRA }, + + { "sfzex.01", 0x020000, 0xe52887c9, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "sfz.02", 0x020000, 0x45f46a08, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + + { "sfz.11m", 0x200000, 0xc4b093cd, CPS2_QSND | BRF_SND }, + { "sfz.12m", 0x200000, 0x8bdbc4b4, CPS2_QSND | BRF_SND }, + + { "sfzj.key", 0x000014, 0x355d85b8, CPS2_ENCRYPTION_KEY }, +}; + +STD_ROM_PICK(Sfzexa) +STD_ROM_FN(Sfzexa) + +struct BurnDriver BurnDrvCpsSfzexa = { + "sfzexa", "sfa", NULL, NULL, "2026", + "Street Fighter Zero EX (Hack, Asia RC1)\0", NULL, "Strygo", "CPS2", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK | BDF_HISCORE_SUPPORTED, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_SF, + NULL, SfzexaRomInfo, SfzexaRomName, NULL, NULL, NULL, NULL, Cps2FightingInputInfo, NULL, + Cps2Init, DrvExit, Cps2Frame, CpsRedraw, CpsAreaScan, + &CpsRecalcPal, 0x1000, 384, 224, 4, 3 +}; + +// Street Fighter Zero EX (Hack, Japan RC1) +// 2026-10-26 +static struct BurnRomInfo SfzexjRomDesc[] = { + { "sfzexj.03c", 0x080000, 0x7eb7adce, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "sfzex.04b", 0x080000, 0x8d9b2480, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "sfzrej.05a", 0x080000, 0xfa9ceda6, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "sfzexj.06", 0x080000, 0x995752fb, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + + { "sfzexj.14m", 0x200000, 0x0a7de3b8, CPS2_GFX | BRF_GRA }, + { "sfzexj.16m", 0x200000, 0xc1b36217, CPS2_GFX | BRF_GRA }, + { "sfzexj.18m", 0x200000, 0x8f468f11, CPS2_GFX | BRF_GRA }, + { "sfzexj.20m", 0x200000, 0x37c3c380, CPS2_GFX | BRF_GRA }, + + { "sfzex.01", 0x020000, 0xe52887c9, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "sfz.02", 0x020000, 0x45f46a08, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + + { "sfz.11m", 0x200000, 0xc4b093cd, CPS2_QSND | BRF_SND }, + { "sfz.12m", 0x200000, 0x8bdbc4b4, CPS2_QSND | BRF_SND }, + + { "sfzj.key", 0x000014, 0x355d85b8, CPS2_ENCRYPTION_KEY }, +}; + +STD_ROM_PICK(Sfzexj) +STD_ROM_FN(Sfzexj) + +struct BurnDriver BurnDrvCpsSfzexj = { + "sfzexj", "sfa", NULL, NULL, "2026", + "Street Fighter Zero EX (Hack, Japan RC1)\0", NULL, "Strygo", "CPS2", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK | BDF_HISCORE_SUPPORTED, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_SF, + NULL, SfzexjRomInfo, SfzexjRomName, NULL, NULL, NULL, NULL, Cps2FightingInputInfo, NULL, + Cps2Init, DrvExit, Cps2Frame, CpsRedraw, CpsAreaScan, + &CpsRecalcPal, 0x1000, 384, 224, 4, 3 +}; + // Street Fighter Alpha 2 Restoration (Hack, USA RC2) // 2026-10-13 static struct BurnRomInfo Sfa2reRomDesc[] = { @@ -17546,29 +17651,29 @@ struct BurnDriver BurnDrvCpsSfz3rej = { &CpsRecalcPal, 0x1000, 384, 224, 4, 3 }; -// Super Street Fighter II Turbo: Grand Master Challenge (Hack, English Restoration RC3) -// 2026-10-21 +// Super Street Fighter II Turbo: Grand Master Challenge (Hack, English Restoration RC4) +// 2026-10-25 static struct BurnRomInfo Ssf2tgmcRomDesc[] = { - { "sfxgmc.03d", 0x080000, 0xee8e9e9b, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "sfxgmc.03d", 0x080000, 0xcfc87616, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, { "sfxgmc.04a", 0x080000, 0xc9df66cd, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, { "sfxj.05", 0x080000, 0xf4ff18f5, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, { "sfxj.06b", 0x080000, 0x413477c2, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, { "sfxj.07a", 0x080000, 0xa18b3d83, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sfxgmc.08", 0x080000, 0xea0d1d90, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "sfxgmc.09", 0x080000, 0x95718f1e, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "sfxgmc.08", 0x080000, 0xf7587a54, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "sfxgmc.09", 0x080000, 0x9a6ae34e, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, { "sfxgmc.13m", 0x200000, 0x75dac0eb, CPS2_GFX | BRF_GRA }, { "sfxgmc.15m", 0x200000, 0x84dcc039, CPS2_GFX | BRF_GRA }, { "sfxgmc.17m", 0x200000, 0xbfcaf206, CPS2_GFX | BRF_GRA }, { "sfxgmc.19m", 0x200000, 0x13a55fd3, CPS2_GFX | BRF_GRA }, - { "sfxgmc.14m", 0x100000, 0x1f8dd3ca, CPS2_GFX | BRF_GRA }, - { "sfxgmc.16m", 0x100000, 0x8033c95d, CPS2_GFX | BRF_GRA }, - { "sfxgmc.18m", 0x100000, 0xdd6cdee2, CPS2_GFX | BRF_GRA }, - { "sfxgmc.20m", 0x100000, 0x4dacc976, CPS2_GFX | BRF_GRA }, - { "sfxgmc.21m", 0x100000, 0x34f361da, CPS2_GFX | BRF_GRA }, - { "sfxgmc.23m", 0x100000, 0x279f2671, CPS2_GFX | BRF_GRA }, - { "sfxgmc.25m", 0x100000, 0x1fbd5b9e, CPS2_GFX | BRF_GRA }, - { "sfxgmc.27m", 0x100000, 0x11725784, CPS2_GFX | BRF_GRA }, + { "sfxgmc.14m", 0x100000, 0xa821a895, CPS2_GFX | BRF_GRA }, + { "sfxgmc.16m", 0x100000, 0xf4dd5566, CPS2_GFX | BRF_GRA }, + { "sfxgmc.18m", 0x100000, 0x6ac0a5bd, CPS2_GFX | BRF_GRA }, + { "sfxgmc.20m", 0x100000, 0x3942554d, CPS2_GFX | BRF_GRA }, + { "sfxgmc.21m", 0x100000, 0xc507ee10, CPS2_GFX | BRF_GRA }, + { "sfxgmc.23m", 0x100000, 0xd0bd63db, CPS2_GFX | BRF_GRA }, + { "sfxgmc.25m", 0x100000, 0x2a8193c4, CPS2_GFX | BRF_GRA }, + { "sfxgmc.27m", 0x100000, 0x3c6e0a1a, CPS2_GFX | BRF_GRA }, { "sfx.01", 0x020000, 0xb47b8835, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, { "sfx.02", 0x020000, 0x0022633f, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, @@ -17584,7 +17689,7 @@ STD_ROM_FN(Ssf2tgmc) struct BurnDriver BurnDrvCpsSsf2tgmc = { "ssf2tgmc", "ssf2t", NULL, NULL, "2026", - "Super Street Fighter II Turbo: Grand Master Challenge (Hack, English Restoration RC3)\0", NULL, "Strygo", "CPS2", + "Super Street Fighter II Turbo: Grand Master Challenge (Hack, English Restoration RC4)\0", NULL, "Strygo", "CPS2", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_CLONE | BDF_HACK | BDF_HISCORE_SUPPORTED, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_SF, NULL, Ssf2tgmcRomInfo, Ssf2tgmcRomName, NULL, NULL, NULL, NULL, Cps2FightingInputInfo, NULL, From 317f163572a1dc65102ba727d4a4d5aaec925102 Mon Sep 17 00:00:00 2001 From: fprietog Date: Tue, 27 Oct 2026 05:07:21 +0100 Subject: [PATCH 12/26] megadrive, neogeo, nes, ngp: various... d_megadrive.cpp: updated the support to: "Black Tiger (HB, v1.9.3)" "Pang (DavidGames) (HB, v1.00)" "Pang (Oldschool Is Beautiful) (HB, v1.00)" "Parasol Stars (HB, v1.0.2)" "Tiger Road (hombreimaginario) (HB, 2026-10-26)" d_neogeo.cpp: updated the support to: "Maiya: Super Nature Girl (HB, v4)" d_nes.cpp: added the support to: "All Hell Unleashed - Ultimate Edition (HB)" "Angry Video Game Nerd 8-bit, The (HB)" d_nes.cpp: updated the support to: "Mad Mix Game R (HB)" d_ngp.cpp: added the support to: "Civilisation (HB)" "Rogue Aces (HB)" "Shut the Box (HB, v0.1)" d_ngp.cpp: updated the support to: "Gardenia (HB, v1.5)" "Neo Bowling (HB, v1.0)" "Over Rev (HB, v3.0)" --- src/burn/drv/megadrive/d_megadrive.cpp | 30 +++++------ src/burn/drv/neogeo/d_neogeo.cpp | 4 +- src/burn/drv/nes/d_nes.cpp | 47 +++++++++++++++-- src/burn/drv/pst90s/d_ngp.cpp | 72 ++++++++++++++++++++++---- 4 files changed, 123 insertions(+), 30 deletions(-) diff --git a/src/burn/drv/megadrive/d_megadrive.cpp b/src/burn/drv/megadrive/d_megadrive.cpp index 01e24ebec..933fffb7f 100644 --- a/src/burn/drv/megadrive/d_megadrive.cpp +++ b/src/burn/drv/megadrive/d_megadrive.cpp @@ -38677,10 +38677,10 @@ struct BurnDriver BurnDrvmd_bioblade = { &bMegadriveRecalcPalette, 0x100, 320, 224, 4, 3 }; -// Black Tiger (HB, v1.9.1) +// Black Tiger (HB, v1.9.3) // https://rester159.itch.io/black-tiger-md static struct BurnRomInfo md_blacktigerRomDesc[] = { - { "Black Tiger v1.9.1 (2026)(rester159).bin", 4194304, 0xa1b19794, BRF_PRG | SEGA_MD_ROM_LOAD16_WORD_SWAP | SEGA_MD_ROM_OFFS_000000 }, + { "Black Tiger v1.9.3 (2026)(rester159).bin", 4194304, 0xe5debe42, BRF_PRG | SEGA_MD_ROM_LOAD16_WORD_SWAP | SEGA_MD_ROM_OFFS_000000 }, }; STD_ROM_PICK(md_blacktiger) @@ -38688,7 +38688,7 @@ STD_ROM_FN(md_blacktiger) struct BurnDriver BurnDrvmd_blacktiger = { "md_blacktiger", NULL, NULL, NULL, "2026", - "Black Tiger (HB, v1.9.1)\0", NULL, "rester159", "Genesis / Mega Drive", + "Black Tiger (HB, v1.9.3)\0", NULL, "rester159", "Genesis / Mega Drive", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_16BIT_ONLY | BDF_HOMEBREW, 1, HARDWARE_SEGA_MEGADRIVE, GBF_PLATFORM | GBF_RUNGUN, 0, MegadriveGetZipName, md_blacktigerRomInfo, md_blacktigerRomName, NULL, NULL, NULL, NULL, MegadriveInputInfo, MegadriveDIPInfo, @@ -42625,10 +42625,10 @@ struct BurnDriver BurnDrvmd_pandajump = { &bMegadriveRecalcPalette, 0x100, 320, 224, 4, 3 }; -// Pang (DavidGames) (HB, Beta v1.03) +// Pang (DavidGames) (HB, v1.00) // https://davidsgame.itch.io/pang-md static struct BurnRomInfo md_pangRomDesc[] = { - { "Pang Beta v1.03 (2026)(DavidGames).bin", 1310720, 0xbdd4517a, BRF_PRG | SEGA_MD_ROM_LOAD16_WORD_SWAP | SEGA_MD_ROM_OFFS_000000 }, + { "Pang v1.00 (2026)(DavidGames).bin", 1310720, 0x2d816dcb, BRF_PRG | SEGA_MD_ROM_LOAD16_WORD_SWAP | SEGA_MD_ROM_OFFS_000000 }, }; STD_ROM_PICK(md_pang) @@ -42636,7 +42636,7 @@ STD_ROM_FN(md_pang) struct BurnDriver BurnDrvmd_pang = { "md_pang", NULL, NULL, NULL, "2026", - "Pang (DavidGames) (HB, Beta v1.03)\0", NULL, "DavidGames", "Genesis / Mega Drive", + "Pang (DavidGames) (HB, v1.00)\0", NULL, "DavidGames", "Genesis / Mega Drive", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_16BIT_ONLY | BDF_HOMEBREW, 2, HARDWARE_SEGA_MEGADRIVE, GBF_ACTION, 0, MegadriveGetZipName, md_pangRomInfo, md_pangRomName, NULL, NULL, NULL, NULL, MegadriveInputInfo, MegadriveDIPInfo, @@ -42644,10 +42644,10 @@ struct BurnDriver BurnDrvmd_pang = { &bMegadriveRecalcPalette, 0x100, 320, 224, 4, 3 }; -// Pang (Oldschool Is Beautiful) (HB, Beta 3) +// Pang (Oldschool Is Beautiful) (HB, v1.00) // https://mlouvet.itch.io/pang-megadrive static struct BurnRomInfo md_pangoibRomDesc[] = { - { "Pang Beta 3 (2026)(Oldschool Is Beautiful).bin", 3877096, 0xa789941b, BRF_PRG | SEGA_MD_ROM_LOAD16_WORD_SWAP | SEGA_MD_ROM_OFFS_000000 }, + { "Pang v1.00 (2026)(Oldschool Is Beautiful).bin", 3909708, 0x2022a547, BRF_PRG | SEGA_MD_ROM_LOAD16_WORD_SWAP | SEGA_MD_ROM_OFFS_000000 }, }; STD_ROM_PICK(md_pangoib) @@ -42655,7 +42655,7 @@ STD_ROM_FN(md_pangoib) struct BurnDriver BurnDrvmd_pangoib = { "md_pangoib", NULL, NULL, NULL, "2026", - "Pang (Oldschool Is Beautiful) (HB, Beta 3)\0", NULL, "Oldschool Is Beautiful", "Genesis / Mega Drive", + "Pang (Oldschool Is Beautiful) (HB, v1.00)\0", NULL, "Oldschool Is Beautiful", "Genesis / Mega Drive", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_16BIT_ONLY | BDF_HOMEBREW, 2, HARDWARE_SEGA_MEGADRIVE, GBF_ACTION, 0, MegadriveGetZipName, md_pangoibRomInfo, md_pangoibRomName, NULL, NULL, NULL, NULL, MegadriveInputInfo, MegadriveDIPInfo, @@ -42952,10 +42952,10 @@ struct BurnDriver BurnDrvmd_papriumunlck = { &bMegadriveRecalcPalette, 0x100, 320, 224, 4, 3 }; -// Parasol Stars (HB, v1.0 RC3) +// Parasol Stars (HB, v1.0.2) // https://mlouvet.itch.io/parasol-stars-megadrive-genesis static struct BurnRomInfo md_parasolRomDesc[] = { - { "Parasol Stars v1.0 RC3 (2026)(Oldschool Is Beautiful).bin", 4194304, 0x181b816a, BRF_PRG | SEGA_MD_ROM_LOAD16_WORD_SWAP | SEGA_MD_ROM_OFFS_000000 }, + { "Parasol Stars v1.0.2 (2026)(Oldschool Is Beautiful).bin", 4194304, 0x72fc1b17, BRF_PRG | SEGA_MD_ROM_LOAD16_WORD_SWAP | SEGA_MD_ROM_OFFS_000000 }, }; STD_ROM_PICK(md_parasol) @@ -42963,7 +42963,7 @@ STD_ROM_FN(md_parasol) struct BurnDriver BurnDrvmd_parasol = { "md_parasol", NULL, NULL, NULL, "2026", - "Parasol Stars (HB, v1.0 RC3)\0", NULL, "Oldschool Is Beautiful", "Genesis / Mega Drive", + "Parasol Stars (HB, v1.0.2)\0", NULL, "Oldschool Is Beautiful", "Genesis / Mega Drive", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_16BIT_ONLY | BDF_HOMEBREW, 2, HARDWARE_SEGA_MEGADRIVE, GBF_PLATFORM, 0, MegadriveGetZipName, md_parasolRomInfo, md_parasolRomName, NULL, NULL, NULL, NULL, MegadriveInputInfo, MegadriveDIPInfo, @@ -44619,10 +44619,10 @@ struct BurnDriver BurnDrvmd_threestooges = { &bMegadriveRecalcPalette, 0x100, 320, 224, 4, 3 }; -// Tiger Road (hombreimaginario) (HB, 2026-10-23) +// Tiger Road (hombreimaginario) (HB, 2026-10-26) // https://hombreimaginario.itch.io/tiger-road-md static struct BurnRomInfo md_tigeroadRomDesc[] = { - { "Tiger Road 2026-10-23 (2026)(hombreimaginario).bin", 2490368, 0x8db153ab, BRF_PRG | SEGA_MD_ROM_LOAD16_WORD_SWAP | SEGA_MD_ROM_OFFS_000000 }, + { "Tiger Road 2026-10-26 (2026)(hombreimaginario).bin", 2490368, 0x98decaf0, BRF_PRG | SEGA_MD_ROM_LOAD16_WORD_SWAP | SEGA_MD_ROM_OFFS_000000 }, }; STD_ROM_PICK(md_tigeroad) @@ -44630,7 +44630,7 @@ STD_ROM_FN(md_tigeroad) struct BurnDriver BurnDrvmd_tigeroad = { "md_tigeroad", NULL, NULL, NULL, "2026", - "Tiger Road (hombreimaginario) (HB, 2026-10-23)\0", NULL, "hombreimaginario", "Genesis / Mega Drive", + "Tiger Road (hombreimaginario) (HB, 2026-10-26)\0", NULL, "hombreimaginario", "Genesis / Mega Drive", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_16BIT_ONLY | BDF_HOMEBREW, 1, HARDWARE_SEGA_MEGADRIVE, GBF_PLATFORM | GBF_SCRFIGHT, 0, MegadriveGetZipName, md_tigeroadRomInfo, md_tigeroadRomName, NULL, NULL, NULL, NULL, MegadriveInputInfo, MegadriveDIPInfo, diff --git a/src/burn/drv/neogeo/d_neogeo.cpp b/src/burn/drv/neogeo/d_neogeo.cpp index 8487f6144..033d60ff3 100644 --- a/src/burn/drv/neogeo/d_neogeo.cpp +++ b/src/burn/drv/neogeo/d_neogeo.cpp @@ -28294,7 +28294,7 @@ struct BurnDriver BurnDrvRobocopi = { BDF_GAME_WORKING | BDF_DEMO, 1, HARDWARE_SNK_NEOGEO, GBF_MISC, 0, NULL, robocopiRomInfo, robocopiRomName, NULL, NULL, NULL, NULL, neogeoInputInfo, neogeouniDIPInfo, NeoInit, NeoExit, NeoFrame, NeoRender, NeoScan, &NeoRecalcPalette, - 0x1000, 304, 224, 4, 3 + 0x1000, 320, 224, 4, 3 }; @@ -30444,7 +30444,7 @@ struct BurnDriver BurnDrvKarnovng = { // Maiya: Super Nature Girl (HB, v4) static struct BurnRomInfo maiyaRomDesc[] = { - { "maiya-p1.p1", 0x080000, 0x5fe28a51, 1 | BRF_ESS | BRF_PRG }, // 0 68K code + { "maiya-p1.p1", 0x080000, 0xecef6baa, 1 | BRF_ESS | BRF_PRG }, // 0 68K code { "maiya-s1.s1", 0x020000, 0x5c7951e1, 2 | BRF_GRA }, // 1 Text layer tiles diff --git a/src/burn/drv/nes/d_nes.cpp b/src/burn/drv/nes/d_nes.cpp index be2e33ccb..79a7ffc11 100644 --- a/src/burn/drv/nes/d_nes.cpp +++ b/src/burn/drv/nes/d_nes.cpp @@ -14699,7 +14699,27 @@ struct BurnDriver BurnDrvnes_aliensmash = { SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT }; +// All Hell Unleashed - Ultimate Edition (HB) +// https://8bitslasher.itch.io/ahu +static struct BurnRomInfo nes_allhellunlueRomDesc[] = { + { "All Hell Unleashed - Ultimate Edition (2025-26)(Fista Productions).nes", 524304, 0x205da9ad, BRF_ESS | BRF_PRG }, +}; + +STD_ROM_PICK(nes_allhellunlue) +STD_ROM_FN(nes_allhellunlue) + +struct BurnDriver BurnDrvnes_allhellunlue = { + "nes_allhellunlue", NULL, NULL, NULL, "2025-26", + "All Hell Unleashed - Ultimate Edition (HB)\0", NULL, "Fista Productions", "NES / Famicom", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_NES, GBF_SCRFIGHT | GBF_VSFIGHT, 0, + NESGetZipName, nes_allhellunlueRomInfo, nes_allhellunlueRomName, NULL, NULL, NULL, NULL, NESInputInfo, NESDIPInfo, + NESInit, NESExit, NESFrame, NESDraw, NESScan, &NESRecalc, 0x40, + SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT +}; + // All Hell Unleashed (HB, v6.66) +// https://8bitslasher.itch.io/ahu static struct BurnRomInfo nes_allhellunl666RomDesc[] = { { "All Hell Unleashed v6.66 (2025)(Fista Productions).nes", 524304, 0x4203e09a, BRF_ESS | BRF_PRG }, }; @@ -14708,16 +14728,17 @@ STD_ROM_PICK(nes_allhellunl666) STD_ROM_FN(nes_allhellunl666) struct BurnDriver BurnDrvnes_allhellunl666 = { - "nes_allhellunl666", NULL, NULL, NULL, "2025", + "nes_allhellunl666", "nes_allhellunlue", NULL, NULL, "2025", "All Hell Unleashed (HB, v6.66)\0", NULL, "Fista Productions", "NES / Famicom", NULL, NULL, NULL, NULL, - BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_NES, GBF_SCRFIGHT | GBF_VSFIGHT, 0, + BDF_GAME_WORKING | BDF_CLONE | BDF_HOMEBREW, 1, HARDWARE_NES, GBF_SCRFIGHT | GBF_VSFIGHT, 0, NESGetZipName, nes_allhellunl666RomInfo, nes_allhellunl666RomName, NULL, NULL, NULL, NULL, NESInputInfo, NESDIPInfo, NESInit, NESExit, NESFrame, NESDraw, NESScan, &NESRecalc, 0x40, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT }; // All Hell Unleashed (HB) +// https://8bitslasher.itch.io/ahu static struct BurnRomInfo nes_allhellunlRomDesc[] = { { "All Hell Unleashed (2025)(Fista Productions).nes", 524304, 0x2f78164f, BRF_ESS | BRF_PRG }, }; @@ -14726,7 +14747,7 @@ STD_ROM_PICK(nes_allhellunl) STD_ROM_FN(nes_allhellunl) struct BurnDriver BurnDrvnes_allhellunl = { - "nes_allhellunl", "nes_allhellunl666", NULL, NULL, "2025", + "nes_allhellunl", "nes_allhellunlue", NULL, NULL, "2025", "All Hell Unleashed (HB)\0", NULL, "Fista Productions", "NES / Famicom", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_CLONE | BDF_HOMEBREW, 1, HARDWARE_NES, GBF_SCRFIGHT | GBF_VSFIGHT, 0, @@ -14898,6 +14919,24 @@ struct BurnDriver BurnDrvnes_ambersa = { SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT }; +// Angry Video Game Nerd 8-bit, The (HB) +static struct BurnRomInfo nes_angrynerdRomDesc[] = { + { "Angry Video Game Nerd 8-bit, The (2025)(Retroware - Mega Cat Studios).nes", 786448, 0xfd8ddfb7, BRF_ESS | BRF_PRG }, +}; + +STD_ROM_PICK(nes_angrynerd) +STD_ROM_FN(nes_angrynerd) + +struct BurnDriver BurnDrvnes_angrynerd = { + "nes_angrynerd", NULL, NULL, NULL, "2025", + "Angry Video Game Nerd 8-bit, The (HB)\0", NULL, "Retroware - Mega Cat Studios", "NES / Famicom", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_NES, GBF_PLATFORM | GBF_RUNGUN, 0, + NESGetZipName, nes_angrynerdRomInfo, nes_angrynerdRomName, NULL, NULL, NULL, NULL, NESInputInfo, NESDIPInfo, + NESInit, NESExit, NESFrame, NESDraw, NESScan, &NESRecalc, 0x40, + SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT +}; + // Anguna: Scourge of the Goblin King (HB) static struct BurnRomInfo nes_angunaRomDesc[] = { { "Anguna - Scourge of the Goblin King (2021)(The 6502 Collective).nes", 524304, 0x1119efff, BRF_ESS | BRF_PRG }, @@ -21542,7 +21581,7 @@ struct BurnDriver BurnDrvnes_machinecave = { // Mad Mix Game R (HB) // https://enriqueggalvez.itch.io/mad-mix-game-r static struct BurnRomInfo nes_madmixgrRomDesc[] = { - { "Mad Mix Game R (2026)(Magic Games).nes", 2097168, 0x58702f7d, BRF_ESS | BRF_PRG }, + { "Mad Mix Game R (2026)(Magic Games).nes", 2097168, 0xbb5d8cc7, BRF_ESS | BRF_PRG }, }; STD_ROM_PICK(nes_madmixgr) diff --git a/src/burn/drv/pst90s/d_ngp.cpp b/src/burn/drv/pst90s/d_ngp.cpp index 497f62357..1f800b3cc 100644 --- a/src/burn/drv/pst90s/d_ngp.cpp +++ b/src/burn/drv/pst90s/d_ngp.cpp @@ -3635,6 +3635,24 @@ struct BurnDriver BurnDrvngpc_cavitm = { 160, 152, 4, 3 }; +// Civilisation (HB) +static struct BurnRomInfo ngpc_civilisationRomDesc[] = { + { "Civilisation (2026)(Chris Ahchay).ngp", 2097152, 0x9392c0d0, 1 | BRF_PRG | BRF_ESS }, // Cartridge +}; + +STDROMPICKEXT(ngpc_civilisation, ngpc_civilisation, ngpc_ngp) +STD_ROM_FN(ngpc_civilisation) + +struct BurnDriver BurnDrvngpc_civilisation = { + "ngp_civilisation", NULL, "ngp_ngp", NULL, "2026", + "Civilisation (HB)\0", NULL, "Chris Ahchay", "NeoGeo Pocket Color", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_SNK_NGPC, GBF_STRATEGY, 0, + NgpGetZipName, ngpc_civilisationRomInfo, ngpc_civilisationRomName, NULL, NULL, NULL, NULL, NgpInputInfo, NgpDIPInfo, + DrvInit, DrvExit, DrvFrame, k1geDraw, DrvScan, &BurnRecalc, 0x1000, + 160, 152, 4, 3 +}; + // Columns (HB) static struct BurnRomInfo ngpc_columnsRomDesc[] = { { "Columns (2007)(Thor).ngp", 196608, 0x7b628e3b, 1 | BRF_PRG | BRF_ESS }, // Cartridge @@ -3815,9 +3833,9 @@ struct BurnDriver BurnDrvngpc_furry = { 160, 152, 4, 3 }; -// Gardenia (HB, v1.4) +// Gardenia (HB, v1.5) static struct BurnRomInfo ngpc_gardeniaRomDesc[] = { - { "Gardenia v1.4 (2026)(Chris Ahchay).ngp", 2097152, 0x6bf8b0aa, 1 | BRF_PRG | BRF_ESS }, // Cartridge + { "Gardenia v1.5 (2026)(Chris Ahchay).ngp", 2097152, 0x1e8081c2, 1 | BRF_PRG | BRF_ESS }, // Cartridge }; STDROMPICKEXT(ngpc_gardenia, ngpc_gardenia, ngpc_ngp) @@ -3825,7 +3843,7 @@ STD_ROM_FN(ngpc_gardenia) struct BurnDriver BurnDrvngpc_gardenia = { "ngp_gardenia", NULL, "ngp_ngp", NULL, "2026", - "Gardenia (HB, v1.4)\0", NULL, "Chris Ahchay", "NeoGeo Pocket Color", + "Gardenia (HB, v1.5)\0", NULL, "Chris Ahchay", "NeoGeo Pocket Color", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_SNK_NGPC, GBF_VERSHOOT, 0, NgpGetZipName, ngpc_gardeniaRomInfo, ngpc_gardeniaRomName, NULL, NULL, NULL, NULL, NgpInputInfo, NgpDIPInfo, @@ -4175,9 +4193,9 @@ struct BurnDriver BurnDrvngpc_neurasm = { 160, 152, 4, 3 }; -// Neo Bowling (HB, v0.9) +// Neo Bowling (HB, v1.0) static struct BurnRomInfo ngpc_neobowlingRomDesc[] = { - { "Neo Bowling v0.9 (2026)(Hardhat Warrior).ngp", 2097152, 0x90e9e1cb, 1 | BRF_PRG | BRF_ESS }, // Cartridge + { "Neo Bowling v1.0 (2026)(Hardhat Warrior).ngp", 2097152, 0xc2eb225c, 1 | BRF_PRG | BRF_ESS }, // Cartridge }; STDROMPICKEXT(ngpc_neobowling, ngpc_neobowling, ngpc_ngp) @@ -4185,7 +4203,7 @@ STD_ROM_FN(ngpc_neobowling) struct BurnDriver BurnDrvngpc_neobowling = { "ngp_neobowling", NULL, "ngp_ngp", NULL, "2026", - "Neo Bowling (HB, v0.9)\0", NULL, "Hardhat Warrior", "NeoGeo Pocket Color", + "Neo Bowling (HB, v1.0)\0", NULL, "Hardhat Warrior", "NeoGeo Pocket Color", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_HOMEBREW, 2, HARDWARE_SNK_NGPC, GBF_SPORTSMISC, 0, NgpGetZipName, ngpc_neobowlingRomInfo, ngpc_neobowlingRomName, NULL, NULL, NULL, NULL, NgpInputInfo, NgpDIPInfo, @@ -4247,9 +4265,9 @@ struct BurnDriver BurnDrvngpc_neotris = { 160, 152, 4, 3 }; -// Over Rev (HB, v2.0) +// Over Rev (HB, v3.0) static struct BurnRomInfo ngpc_overrevRomDesc[] = { - { "Over Rev v2.0 (2026)(Tixul).ngp", 2097152, 0xbd6dcde5, 1 | BRF_PRG | BRF_ESS }, // Cartridge + { "Over Rev v3.0 (2026)(Tixul).ngp", 2097152, 0x8539834d, 1 | BRF_PRG | BRF_ESS }, // Cartridge }; STDROMPICKEXT(ngpc_overrev, ngpc_overrev, ngpc_ngp) @@ -4257,7 +4275,7 @@ STD_ROM_FN(ngpc_overrev) struct BurnDriver BurnDrvngpc_overrev = { "ngp_overrev", NULL, "ngp_ngp", NULL, "2026", - "Over Rev (HB, v2.0)\0", NULL, "Tixul", "NeoGeo Pocket Color", + "Over Rev (HB, v3.0)\0", NULL, "Tixul", "NeoGeo Pocket Color", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_HOMEBREW, 2, HARDWARE_SNK_NGPC, GBF_RACING, 0, NgpGetZipName, ngpc_overrevRomInfo, ngpc_overrevRomName, NULL, NULL, NULL, NULL, NgpInputInfo, NgpDIPInfo, @@ -4355,6 +4373,24 @@ struct BurnDriver BurnDrvngpc_robotron = { 160, 152, 4, 3 }; +// Rogue Aces (HB) +static struct BurnRomInfo ngpc_rogueacesRomDesc[] = { + { "Rogue Aces (2026)(Chris Ahchay).ngp", 2097152, 0x6ea9eb85, 1 | BRF_PRG | BRF_ESS }, // Cartridge +}; + +STDROMPICKEXT(ngpc_rogueaces, ngpc_rogueaces, ngpc_ngp) +STD_ROM_FN(ngpc_rogueaces) + +struct BurnDriver BurnDrvngpc_rogueaces = { + "ngp_rogueaces", NULL, "ngp_ngp", NULL, "2026", + "Rogue Aces (HB)\0", NULL, "Chris Ahchay", "NeoGeo Pocket Color", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_SNK_NGPC, GBF_MULTISHOOT, 0, + NgpGetZipName, ngpc_rogueacesRomInfo, ngpc_rogueacesRomName, NULL, NULL, NULL, NULL, NgpInputInfo, NgpDIPInfo, + DrvInit, DrvExit, DrvFrame, k1geDraw, DrvScan, &BurnRecalc, 0x1000, + 160, 152, 4, 3 +}; + // Ruptus Mini (HB) static struct BurnRomInfo ngpc_ruptusmRomDesc[] = { { "Ruptus Mini (2025)(Inufuto).ngp", 524288, 0x09f1b098, 1 | BRF_PRG | BRF_ESS }, // Cartridge @@ -4391,6 +4427,24 @@ struct BurnDriver BurnDrvngpc_samegame = { 160, 152, 4, 3 }; +// Shut the Box (HB, v0.1) +static struct BurnRomInfo ngpc_shutboxRomDesc[] = { + { "Shut the Box v0.1 (2026)(Hardhat Warrior).ngp", 2097152, 0xe89d2a9c, 1 | BRF_PRG | BRF_ESS }, // Cartridge +}; + +STDROMPICKEXT(ngpc_shutbox, ngpc_shutbox, ngpc_ngp) +STD_ROM_FN(ngpc_shutbox) + +struct BurnDriver BurnDrvngpc_shutbox = { + "ngp_shutbox", NULL, "ngp_ngp", NULL, "2026", + "Shut the Box (HB, v0.1)\0", NULL, "Hardhat Warrior", "NeoGeo Pocket Color", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_HOMEBREW, 2, HARDWARE_SNK_NGPC, GBF_BOARD, 0, + NgpGetZipName, ngpc_shutboxRomInfo, ngpc_shutboxRomName, NULL, NULL, NULL, NULL, NgpInputInfo, NgpDIPInfo, + DrvInit, DrvExit, DrvFrame, k1geDraw, DrvScan, &BurnRecalc, 0x1000, + 160, 152, 4, 3 +}; + // Snake (HB, v1.2) static struct BurnRomInfo ngpc_snakeRomDesc[] = { { "Snake v1.2 (2000)(Ivan Mackintosh).ngp", 524288, 0x82510aa9, 1 | BRF_PRG | BRF_ESS }, // Cartridge From 56df5336d1c835d38dbcee9a1299e8a0fdce16a7 Mon Sep 17 00:00:00 2001 From: dinkc64 Date: Tue, 27 Oct 2026 02:03:34 -0400 Subject: [PATCH 13/26] dx11 blitter: during ffwd temporarily disable vsync --- src/burner/win32/burner_win32.h | 1 + src/burner/win32/run.cpp | 4 ++++ src/intf/video/win32/vid_d3d11.cpp | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/burner/win32/burner_win32.h b/src/burner/win32/burner_win32.h index 7cff0f636..bb1197bb7 100644 --- a/src/burner/win32/burner_win32.h +++ b/src/burner/win32/burner_win32.h @@ -269,6 +269,7 @@ int RunFrame(int bDraw, int bPause); int RunMessageLoop(); int RunReset(); void ToggleLayer(unsigned char thisLayer); +bool EmulatorAppIsFast(); // scrn.cpp extern HWND hScrnWnd; // Handle to the screen window diff --git a/src/burner/win32/run.cpp b/src/burner/win32/run.cpp index 6cad4c09c..bd781514d 100644 --- a/src/burner/win32/run.cpp +++ b/src/burner/win32/run.cpp @@ -60,6 +60,10 @@ struct lua_hotkey_handler hotkey_debounces[] = { { NULL, 0, 0, NULL } }; +bool EmulatorAppIsFast() { + return bAppDoFast; +} + void EmulatorAppDoFast(bool dofast) { bAppDoFast = dofast; bAppDoFasttoggled = 0; diff --git a/src/intf/video/win32/vid_d3d11.cpp b/src/intf/video/win32/vid_d3d11.cpp index dbda1d432..a5d025201 100644 --- a/src/intf/video/win32/vid_d3d11.cpp +++ b/src/intf/video/win32/vid_d3d11.cpp @@ -3447,7 +3447,7 @@ static int d3d11Paint(int bValidate) return 1; } - HRESULT hr = pSwapChain->Present(bVidVSync ? 1 : 0, 0); + HRESULT hr = pSwapChain->Present((bVidVSync && !EmulatorAppIsFast()) ? 1 : 0, 0); bInPaint = false; From e8eabacd0d1b2046bf013281f26cd1f7344f4053 Mon Sep 17 00:00:00 2001 From: dinkc64 Date: Tue, 27 Oct 2026 02:15:43 -0400 Subject: [PATCH 14/26] add experimental wasapi audio output device --- makefile.burner_win32_rules | 2 +- src/burner/win32/app.rc | 13 + src/burner/win32/cona.cpp | 10 +- src/burner/win32/menu.cpp | 30 +- src/burner/win32/resource.h | 5 + src/burner/win32/scrn.cpp | 28 ++ src/intf/audio/aud_interface.cpp | 4 +- src/intf/audio/win32/aud_wasapi.cpp | 739 ++++++++++++++++++++++++++++ 8 files changed, 827 insertions(+), 4 deletions(-) create mode 100644 src/intf/audio/win32/aud_wasapi.cpp diff --git a/makefile.burner_win32_rules b/makefile.burner_win32_rules index e8113bfc4..3a7ed4820 100644 --- a/makefile.burner_win32_rules +++ b/makefile.burner_win32_rules @@ -27,7 +27,7 @@ depobj += about.o bzip.o choose_monitor.o cona.o d3d11shaderparams.o debugger.o \ 2xpm.o 2xsai.o crt.o ddt3x.o epx.o hq2xs.o hq2xs_16.o hq3xs.o hq_shared32.o xbr.o xbr32.o \ \ - aud_dsound3.o aud_xaudio2.o cd_img.o ddraw_core.o dinput_core.o directx9_core.o dsound_core.o \ + aud_dsound3.o aud_xaudio2.o aud_wasapi.o cd_img.o ddraw_core.o dinput_core.o directx9_core.o dsound_core.o \ inp_dinput.o inp_xinput.o prf_performance_counter.o vid_d3d.o vid_d3d11.o vid_d3d11_shader.o vid_ddraw.o vid_ddrawfx.o vid_effect.o vid_directx9.o vid_directx_support.o ifdef INCLUDE_7Z_SUPPORT diff --git a/src/burner/win32/app.rc b/src/burner/win32/app.rc index e739c334a..00f86eea9 100644 --- a/src/burner/win32/app.rc +++ b/src/burner/win32/app.rc @@ -1325,6 +1325,7 @@ BEGIN BEGIN MENUITEM "DirectSound", MENU_AUD_PLUGIN_1 MENUITEM "XAudio2", MENU_AUD_PLUGIN_2 + MENUITEM "WASAPI", MENU_AUD_PLUGIN_3 END MENUITEM "Plugin &options", MENU_AUDIO_OPTIONS, GRAYED POPUP "&Audio Volume" @@ -1992,6 +1993,18 @@ BEGIN END END +IDR_MENU_AUD_PLUGIN_3 MENU +BEGIN + POPUP "WASAPI plugin" + BEGIN + MENUITEM "Use Low-pass filter", MENU_WASAPI_BASS + MENUITEM SEPARATOR + MENUITEM "Sound disabled", MENU_WASAPI_NOSOUND + MENUITEM "44100Hz samplerate", MENU_WASAPI_44100 + MENUITEM "48000Hz samplerate", MENU_WASAPI_48000 + END +END + IDR_MENU_GAMESEL MENU BEGIN POPUP "Game Selection Menu" diff --git a/src/burner/win32/cona.cpp b/src/burner/win32/cona.cpp index 4abdace71..b492ca2f8 100644 --- a/src/burner/win32/cona.cpp +++ b/src/burner/win32/cona.cpp @@ -376,6 +376,8 @@ int ConfigAppLoad() VAR(nAudDSPModule[0]); VAR(nAudSampleRate[1]); VAR(nAudDSPModule[1]); + VAR(nAudSampleRate[2]); + VAR(nAudDSPModule[2]); // Other STR(szPlaceHolder); @@ -784,7 +786,7 @@ int ConfigAppSave() VAR(nInputSelect); _ftprintf(h, _T("\n")); _ftprintf(h, _T("// --- Sound ------------------------------------------------------------------\n")); - _ftprintf(h, _T("\n// The selected audio plugin\n")); + _ftprintf(h, _T("\n// The selected audio plugin (0 = DirectSound, 1 = XAudio2, 2 = WASAPI)\n")); VAR(nAudSelect); _ftprintf(h, _T("\n// Audio Volume\n")); VAR(nAudVolume); @@ -806,6 +808,12 @@ int ConfigAppSave() VAR(nAudSampleRate[1]); _ftprintf(h, _T("\n// DSP module to use for sound enhancement: 0 = none, 1 = low-pass filter, 2 = reverb\n")); VAR(nAudDSPModule[1]); + _ftprintf(h, _T("\n")); + _ftprintf(h, _T("// --- WASAPI plugin settings -------------------------------------------------\n")); + _ftprintf(h, _T("\n// Sample rate\n")); + VAR(nAudSampleRate[2]); + _ftprintf(h, _T("\n// DSP module to use for sound enhancement: 0 = none, 1 = low-pass filter\n")); + VAR(nAudDSPModule[2]); _ftprintf(h, _T("\n\n\n")); _ftprintf(h, _T("// --- UI ---------------------------------------------------------------------\n")); diff --git a/src/burner/win32/menu.cpp b/src/burner/win32/menu.cpp index 5ad09ec1e..2979386ab 100644 --- a/src/burner/win32/menu.cpp +++ b/src/burner/win32/menu.cpp @@ -728,6 +728,7 @@ int MenuCreate() hAudioPluginMenu[0] = FBALoadMenu(hAppInst, MAKEINTRESOURCE(IDR_MENU_AUD_PLUGIN_1)); hAudioPluginMenu[1] = FBALoadMenu(hAppInst, MAKEINTRESOURCE(IDR_MENU_AUD_PLUGIN_2)); + hAudioPluginMenu[2] = FBALoadMenu(hAppInst, MAKEINTRESOURCE(IDR_MENU_AUD_PLUGIN_3)); } if (hMenuPopup == NULL) { @@ -1417,6 +1418,25 @@ void MenuUpdate() EnableMenuItem(hMenu, MENU_AUDIO_VOLUME_200, MF_ENABLED | MF_BYCOMMAND); break; } + + case 2: { + var = MENU_WASAPI_NOSOUND; + if (nAudSampleRate[2] > 0) { + if (nAudSampleRate[2] <= 44100) { + var = MENU_WASAPI_44100; + nAudSampleRate[2] = 44100; // sanity + } else { + var = MENU_WASAPI_48000; + nAudSampleRate[2] = 48000; + } + } + CheckMenuRadioItem(hMenu, MENU_WASAPI_NOSOUND, MENU_WASAPI_48000, var, MF_BYCOMMAND); + CheckMenuItem(hMenu, MENU_WASAPI_BASS, (nAudDSPModule[2] & 1) ? MF_CHECKED : MF_UNCHECKED); + // WASAPI is able to amp volume above 0db, enable: + EnableMenuItem(hMenu, MENU_AUDIO_VOLUME_150, MF_ENABLED | MF_BYCOMMAND); + EnableMenuItem(hMenu, MENU_AUDIO_VOLUME_200, MF_ENABLED | MF_BYCOMMAND); + break; + } } var = MENU_INTERPOLATE_0 + nInterpolation; @@ -1689,7 +1709,7 @@ void MenuEnableItems() SetMenuItemInfo(GetSubMenu(hMenu, 1), 1, TRUE, &myMenuItemInfo); } - if (hBlitterMenu[nAudSelect]) { + if (hAudioPluginMenu[nAudSelect]) { MENUITEMINFO myMenuItemInfo; myMenuItemInfo.cbSize = sizeof(MENUITEMINFO); myMenuItemInfo.fMask = MIIM_SUBMENU | MIIM_STATE; @@ -1766,6 +1786,9 @@ void MenuEnableItems() EnableMenuItem(hMenu, MENU_XAUDIO_NOSOUND, MF_GRAYED | MF_BYCOMMAND); EnableMenuItem(hMenu, MENU_XAUDIO_44100, MF_GRAYED | MF_BYCOMMAND); EnableMenuItem(hMenu, MENU_XAUDIO_48000, MF_GRAYED | MF_BYCOMMAND); + EnableMenuItem(hMenu, MENU_WASAPI_NOSOUND, MF_GRAYED | MF_BYCOMMAND); + EnableMenuItem(hMenu, MENU_WASAPI_44100, MF_GRAYED | MF_BYCOMMAND); + EnableMenuItem(hMenu, MENU_WASAPI_48000, MF_GRAYED | MF_BYCOMMAND); EnableMenuItem(hMenu, MENU_INTERPOLATE_FM_0, MF_GRAYED | MF_BYCOMMAND); EnableMenuItem(hMenu, MENU_INTERPOLATE_FM_3, MF_GRAYED | MF_BYCOMMAND); EnableMenuItem(hMenu, MENU_FRAMES, MF_GRAYED | MF_BYCOMMAND); @@ -1783,6 +1806,7 @@ void MenuEnableItems() EnableMenuItem(hMenu, MENU_AUD_PLUGIN_1, MF_GRAYED | MF_BYCOMMAND); EnableMenuItem(hMenu, MENU_AUD_PLUGIN_2, MF_GRAYED | MF_BYCOMMAND); + EnableMenuItem(hMenu, MENU_AUD_PLUGIN_3, MF_GRAYED | MF_BYCOMMAND); EnableMenuItem(hMenu, MENU_INP_PLUGIN_1, MF_GRAYED | MF_BYCOMMAND); EnableMenuItem(hMenu, MENU_INP_PLUGIN_2, MF_GRAYED | MF_BYCOMMAND); @@ -2080,6 +2104,9 @@ void MenuEnableItems() EnableMenuItem(hMenu, MENU_XAUDIO_NOSOUND, MF_ENABLED | MF_BYCOMMAND); EnableMenuItem(hMenu, MENU_XAUDIO_44100, MF_ENABLED | MF_BYCOMMAND); EnableMenuItem(hMenu, MENU_XAUDIO_48000, MF_ENABLED | MF_BYCOMMAND); + EnableMenuItem(hMenu, MENU_WASAPI_NOSOUND, MF_ENABLED | MF_BYCOMMAND); + EnableMenuItem(hMenu, MENU_WASAPI_44100, MF_ENABLED | MF_BYCOMMAND); + EnableMenuItem(hMenu, MENU_WASAPI_48000, MF_ENABLED | MF_BYCOMMAND); // EnableMenuItem(hMenu, MENU_INTERPOLATE_1, MF_ENABLED | MF_BYCOMMAND); // EnableMenuItem(hMenu, MENU_INTERPOLATE_3, MF_ENABLED | MF_BYCOMMAND); EnableMenuItem(hMenu, MENU_WLOGSTART, MF_GRAYED | MF_BYCOMMAND); @@ -2100,6 +2127,7 @@ void MenuEnableItems() EnableMenuItem(hMenu, MENU_AUD_PLUGIN_1, MF_ENABLED | MF_BYCOMMAND); EnableMenuItem(hMenu, MENU_AUD_PLUGIN_2, MF_ENABLED | MF_BYCOMMAND); + EnableMenuItem(hMenu, MENU_AUD_PLUGIN_3, MF_ENABLED | MF_BYCOMMAND); EnableMenuItem(hMenu, MENU_INP_PLUGIN_1, MF_ENABLED | MF_BYCOMMAND); EnableMenuItem(hMenu, MENU_INP_PLUGIN_2, MF_ENABLED | MF_BYCOMMAND); diff --git a/src/burner/win32/resource.h b/src/burner/win32/resource.h index 095321e4f..dcf9ffa64 100644 --- a/src/burner/win32/resource.h +++ b/src/burner/win32/resource.h @@ -68,6 +68,7 @@ #define IDR_MENU_BLITTER_6 115 #define IDR_MENU_AUD_PLUGIN_1 130 #define IDR_MENU_AUD_PLUGIN_2 131 +#define IDR_MENU_AUD_PLUGIN_3 132 #define IDR_MENU_GAMESEL 140 #define IDR_MENU_LUAWINDOW 150 #define IDR_ACCELERATOR 160 @@ -658,12 +659,16 @@ #define MENU_DSOUND_BASS 10201 #define MENU_XAUDIO_BASS 10202 #define MENU_XAUDIO_REVERB 10203 +#define MENU_WASAPI_BASS 10204 #define MENU_DSOUND_NOSOUND 10210 #define MENU_DSOUND_44100 10213 #define MENU_DSOUND_48000 10214 #define MENU_XAUDIO_NOSOUND 10220 #define MENU_XAUDIO_44100 10223 #define MENU_XAUDIO_48000 10224 +#define MENU_WASAPI_NOSOUND 10225 +#define MENU_WASAPI_44100 10226 +#define MENU_WASAPI_48000 10227 #define MENU_INTERPOLATE_0 10230 #define MENU_INTERPOLATE_1 10231 #define MENU_INTERPOLATE_3 10233 diff --git a/src/burner/win32/scrn.cpp b/src/burner/win32/scrn.cpp index d32c2f462..083f5bebe 100644 --- a/src/burner/win32/scrn.cpp +++ b/src/burner/win32/scrn.cpp @@ -2932,6 +2932,11 @@ static void OnCommand(HWND /*hDlg*/, int id, HWND /*hwndCtl*/, UINT codeNotify) POST_INITIALISE_MESSAGE; break; + case MENU_AUD_PLUGIN_3: + AudSelect(2); + POST_INITIALISE_MESSAGE; + break; + case MENU_DSOUND_NOSOUND: if (!bDrvOkay) { nAudSampleRate[0] = 0; @@ -2970,6 +2975,25 @@ static void OnCommand(HWND /*hDlg*/, int id, HWND /*hwndCtl*/, UINT codeNotify) } break; + case MENU_WASAPI_NOSOUND: + if (!bDrvOkay) { + nAudSampleRate[2] = 0; + POST_INITIALISE_MESSAGE; + } + break; + case MENU_WASAPI_44100: + if (!bDrvOkay) { + nAudSampleRate[2] = 44100; + POST_INITIALISE_MESSAGE; + } + break; + case MENU_WASAPI_48000: + if (!bDrvOkay) { + nAudSampleRate[2] = 48000; + POST_INITIALISE_MESSAGE; + } + break; + case MENU_FRAMES: if (UseDialogs()) { if (!bDrvOkay) { @@ -3012,6 +3036,10 @@ static void OnCommand(HWND /*hDlg*/, int id, HWND /*hwndCtl*/, UINT codeNotify) nAudDSPModule[1] ^= 2; break; + case MENU_WASAPI_BASS: + nAudDSPModule[2] ^= 1; + break; + case MENU_WLOGSTART: AudBlankSound(); WaveLogStart(); diff --git a/src/intf/audio/aud_interface.cpp b/src/intf/audio/aud_interface.cpp index b1b9fa2d7..b831bfdf2 100644 --- a/src/intf/audio/aud_interface.cpp +++ b/src/intf/audio/aud_interface.cpp @@ -1,7 +1,7 @@ // Audio Output #include "burner.h" -INT32 nAudSampleRate[8] = { 44100, 44100, 22050, 22050, 22050, 22050, 22050, 22050 }; // sample rate +INT32 nAudSampleRate[8] = { 44100, 44100, 48000, 22050, 22050, 22050, 22050, 22050 }; // sample rate INT32 nAudVolume = 10000; // Sound volume (% * 100) INT32 nAudSegCount = 6; // Segs in the pdsbLoop buffer INT32 nAudSegLen = 0; // Seg length in samples (calculated from Rate/Fps) @@ -19,6 +19,7 @@ static UINT32 nAudActive = 0; #if defined (BUILD_WIN32) extern struct AudOut AudOutDx; extern struct AudOut AudOutXAudio2; + extern struct AudOut AudOutWASAPI; #elif defined (BUILD_MACOS) extern struct AudOut AudOutMacOS; #elif defined (BUILD_SDL) @@ -43,6 +44,7 @@ static struct AudOut *pAudOut[]= #if defined (BUILD_WIN32) &AudOutDx, &AudOutXAudio2, + &AudOutWASAPI, #elif defined (BUILD_MACOS) &AudOutMacOS, #elif defined (BUILD_SDL) diff --git a/src/intf/audio/win32/aud_wasapi.cpp b/src/intf/audio/win32/aud_wasapi.cpp new file mode 100644 index 000000000..2e9ba7764 --- /dev/null +++ b/src/intf/audio/win32/aud_wasapi.cpp @@ -0,0 +1,739 @@ +// WASAPI (Windows Audio Session API) audio output module for FBNeo +// - dink 2026 +#include "burner.h" +#include "aud_dsp.h" + +#include +#include + +#ifdef _MSC_VER +#pragma comment(lib, "ole32") +#endif + +// These flags are missing from some older SDK / MinGW headers +#ifndef AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM +#define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 +#endif +#ifndef AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY +#define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 +#endif +#ifndef AUDCLNT_E_DEVICE_INVALIDATED +#define AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004L) +#endif +#ifndef AUDCLNT_E_RESOURCES_INVALIDATED +#define AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026L) +#endif +#ifndef WAVE_FORMAT_IEEE_FLOAT +#define WAVE_FORMAT_IEEE_FLOAT 0x0003 +#endif +#ifndef WAVE_FORMAT_EXTENSIBLE +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE +#endif + +// GUIDs are defined locally so we don't depend on __uuidof() or the uuid/ksuser libs +static const GUID FBN_CLSID_MMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c, { 0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e } }; +static const GUID FBN_IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35, { 0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6 } }; +static const GUID FBN_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } }; +static const GUID FBN_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483, { 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } }; +static const GUID FBN_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; +static const GUID FBN_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; +// PKEY_Device_FriendlyName +static const PROPERTYKEY FBN_PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd, { 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0 } }, 14 }; + +#define WASAPI_PLUGIN 2 // our index in nAudSampleRate[] / nAudDSPModule[] +#define REFTIMES_PER_SEC 10000000 + +static IMMDeviceEnumerator* pEnumerator = NULL; +static IMMDevice* pDevice = NULL; +static IAudioClient* pAudioClient = NULL; +static IAudioRenderClient* pRenderClient = NULL; +static HANDLE hWasapiEvent = NULL; + +int (*WasapiGetNextSound)(int); // Callback used to request more sound +static int WasapiSetVolume(); // forward + +static int nWasapiFps = 0; // Application fps * 100 +static float fWasapiVol = 1.0f; // Software volume (allows > 100%) + +static UINT32 nBufferFrames = 0; // Size of the WASAPI buffer (frames) +static UINT32 nTargetFrames = 0; // How full we keep the buffer (frames) = latency + +static bool bStreamStarted = false; +static bool bEventDriven = false; // false: fall back to polling +static bool bBlanked = false; // AudBlankSound() was called during SoundCheck + +// Output format actually used by the stream +enum { FMT_S16 = 0, FMT_S24, FMT_S32, FMT_F32 }; +static int nOutFormat = FMT_S16; +static int nOutChannels = 2; +static int nOutBlockAlign = 4; +static bool bAutoConvert = false; + +static TCHAR szDeviceName[128] = _T(""); + +// Device-lost handling +static bool bDeviceLost = false; +static DWORD nLastReopenTry = 0; +static DWORD nLostLastFrame = 0; + +static int WasapiGetNextSoundFiller(int) +{ + if (nAudNextSound == NULL) { + return 1; + } + AudWriteSilence(); + + return 0; +} + +static int WasapiSetCallback(int (*pCallback)(int)) +{ + if (pCallback == NULL) { + WasapiGetNextSound = WasapiGetNextSoundFiller; + } else { + WasapiGetNextSound = pCallback; + } + + return 0; +} + +// Sample conversion: 16-bit stereo -> device format, with software volume + +static inline INT32 WasapiClip16(INT32 s) +{ + if (s > 32767) return 32767; + if (s < -32768) return -32768; + return s; +} + +static void WasapiConvert(BYTE* pDst, const INT16* pSrc, UINT32 nFrames) +{ + const bool bFullVol = (fWasapiVol == 1.0f); + const INT32 nVol = (INT32)(fWasapiVol * 65536.0f); // 16.16 fixed point + + for (UINT32 i = 0; i < nFrames; i++) { + INT32 l = pSrc[i * 2 + 0]; + INT32 r = pSrc[i * 2 + 1]; + + if (!bFullVol) { + l = WasapiClip16((INT32)(((INT64)l * nVol) >> 16)); + r = WasapiClip16((INT32)(((INT64)r * nVol) >> 16)); + } + + switch (nOutFormat) { + case FMT_S16: { + INT16* d = (INT16*)pDst; + d[0] = (INT16)l; + d[1] = (INT16)r; + for (int c = 2; c < nOutChannels; c++) d[c] = 0; + break; + } + case FMT_S24: { + BYTE* d = pDst; + INT32 v[2] = { l << 8, r << 8 }; + for (int c = 0; c < 2; c++) { + d[c * 3 + 0] = (BYTE)(v[c] & 0xff); + d[c * 3 + 1] = (BYTE)((v[c] >> 8) & 0xff); + d[c * 3 + 2] = (BYTE)((v[c] >> 16) & 0xff); + } + memset(d + 6, 0, (nOutChannels - 2) * 3); + break; + } + case FMT_S32: { + INT32* d = (INT32*)pDst; + d[0] = l << 16; + d[1] = r << 16; + for (int c = 2; c < nOutChannels; c++) d[c] = 0; + break; + } + case FMT_F32: { + float* d = (float*)pDst; + d[0] = (float)l * (1.0f / 32768.0f); + d[1] = (float)r * (1.0f / 32768.0f); + for (int c = 2; c < nOutChannels; c++) d[c] = 0.0f; + break; + } + } + + pDst += nOutBlockAlign; + } +} + +// Device / stream setup + +static void WasapiReleaseStream() +{ + if (pAudioClient && bStreamStarted) { + pAudioClient->Stop(); + } + bStreamStarted = false; + + RELEASE(pRenderClient); + RELEASE(pAudioClient); + RELEASE(pDevice); + RELEASE(pEnumerator); +} + +static void WasapiGetDeviceName() +{ + szDeviceName[0] = 0; + + IPropertyStore* pProps = NULL; + if (pDevice && SUCCEEDED(pDevice->OpenPropertyStore(STGM_READ, &pProps))) { + PROPVARIANT varName; + PropVariantInit(&varName); + if (SUCCEEDED(pProps->GetValue(FBN_PKEY_Device_FriendlyName, &varName)) && varName.vt == VT_LPWSTR && varName.pwszVal) { +#if defined (_UNICODE) + wcsncpy(szDeviceName, varName.pwszVal, 127); +#else + WideCharToMultiByte(CP_ACP, 0, varName.pwszVal, -1, szDeviceName, 128, NULL, NULL); +#endif + szDeviceName[127] = 0; + } + PropVariantClear(&varName); + pProps->Release(); + } +} + +// Work out how to feed a given (mix) format; returns 0 if we can handle it +static int WasapiParseFormat(const WAVEFORMATEX* pwfx) +{ + if (pwfx->nChannels < 2) { + return 1; + } + + bool bFloat = false; + bool bPCM = false; + + if (pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { + bFloat = true; + } else if (pwfx->wFormatTag == WAVE_FORMAT_PCM) { + bPCM = true; + } else if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE && pwfx->cbSize >= 22) { + const WAVEFORMATEXTENSIBLE* pwfxe = (const WAVEFORMATEXTENSIBLE*)pwfx; + if (IsEqualGUID(pwfxe->SubFormat, FBN_SUBTYPE_IEEE_FLOAT)) bFloat = true; + if (IsEqualGUID(pwfxe->SubFormat, FBN_SUBTYPE_PCM)) bPCM = true; + } + + if (bFloat && pwfx->wBitsPerSample == 32) { + nOutFormat = FMT_F32; + } else if (bPCM && pwfx->wBitsPerSample == 16) { + nOutFormat = FMT_S16; + } else if (bPCM && pwfx->wBitsPerSample == 24) { + nOutFormat = FMT_S24; + } else if (bPCM && pwfx->wBitsPerSample == 32) { + nOutFormat = FMT_S32; + } else { + return 1; + } + + nOutChannels = pwfx->nChannels; + nOutBlockAlign = pwfx->nBlockAlign; + + return 0; +} + +// Activate a fresh IAudioClient on pDevice and initialise it. A client can only be +// Initialize()d once, so every attempt gets its own instance. +static HRESULT WasapiTryInit(const WAVEFORMATEX* pwfx, DWORD nFlags, REFERENCE_TIME hnsBuffer) +{ + RELEASE(pAudioClient); + + HRESULT hr = pDevice->Activate(FBN_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); + if (FAILED(hr)) { + return hr; + } + + hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, nFlags, hnsBuffer, 0, pwfx, NULL); + if (FAILED(hr)) { + RELEASE(pAudioClient); + } + + return hr; +} + +static int WasapiOpenStream() +{ + HRESULT hr; + + if (FAILED(hr = CoCreateInstance(FBN_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, FBN_IID_IMMDeviceEnumerator, (void**)&pEnumerator))) { + return 1; + } + + if (FAILED(hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice))) { + WasapiReleaseStream(); + return 1; + } + + WasapiGetDeviceName(); + + // The buffer we ask for holds nAudSegCount frames of emulation - same latency as the other plugins. + REFERENCE_TIME hnsBuffer = (REFERENCE_TIME)REFTIMES_PER_SEC * nAudSegLen * nAudSegCount / nAudSampleRate[WASAPI_PLUGIN]; + + // Our native format: 16-bit stereo PCM at the selected rate + WAVEFORMATEX wfx; + memset(&wfx, 0, sizeof(wfx)); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 2; + wfx.nSamplesPerSec = nAudSampleRate[WASAPI_PLUGIN]; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = 4; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + wfx.cbSize = 0; + + const DWORD nAutoConv = AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; + + bEventDriven = true; + bAutoConvert = true; + nOutFormat = FMT_S16; nOutChannels = 2; nOutBlockAlign = 4; + + // 1) 16-bit stereo, let Windows resample/convert to the mix format (Win7+) + hr = WasapiTryInit(&wfx, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | nAutoConv, hnsBuffer); + + // 2) Device mix format, converted by us (only possible if the rate matches) + if (FAILED(hr)) { + WAVEFORMATEX* pMix = NULL; + IAudioClient* pTmp = NULL; + + bAutoConvert = false; + + if (SUCCEEDED(pDevice->Activate(FBN_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pTmp))) { + if (SUCCEEDED(pTmp->GetMixFormat(&pMix)) && pMix) { + if (pMix->nSamplesPerSec == (DWORD)nAudSampleRate[WASAPI_PLUGIN] && WasapiParseFormat(pMix) == 0) { + hr = WasapiTryInit(pMix, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, hnsBuffer); + if (FAILED(hr)) { + bEventDriven = false; + hr = WasapiTryInit(pMix, 0, hnsBuffer); + } + } + CoTaskMemFree(pMix); + } + pTmp->Release(); + } + } + + // 3) Last resort: auto-convert without event callback (polling) + if (FAILED(hr)) { + bEventDriven = false; + bAutoConvert = true; + nOutFormat = FMT_S16; nOutChannels = 2; nOutBlockAlign = 4; + hr = WasapiTryInit(&wfx, nAutoConv, hnsBuffer); + } + + if (FAILED(hr) || pAudioClient == NULL) { +#ifdef PRINT_DEBUG_INFO + bprintf(0, _T("*** WASAPI: unable to initialise audio client (0x%08x).\n"), hr); +#endif + WasapiReleaseStream(); + return 1; + } + + if (bEventDriven) { + if (FAILED(pAudioClient->SetEventHandle(hWasapiEvent))) { + WasapiReleaseStream(); + return 1; + } + } + + if (FAILED(pAudioClient->GetBufferSize(&nBufferFrames))) { + WasapiReleaseStream(); + return 1; + } + + if (FAILED(pAudioClient->GetService(FBN_IID_IAudioRenderClient, (void**)&pRenderClient))) { + WasapiReleaseStream(); + return 1; + } + + // Keep the buffer filled up to nAudSegCount segs (or as much as fits) + nTargetFrames = nAudSegLen * nAudSegCount; + if (nTargetFrames > nBufferFrames) { + nTargetFrames = nBufferFrames; + } + if (nTargetFrames < (UINT32)nAudSegLen) { + // WASAPI gave us a buffer smaller than one frame of sound - can't work with that + WasapiReleaseStream(); + return 1; + } + + return 0; +} + +// Fill the buffer up to the target with silence +static void WasapiPrefill() +{ + UINT32 nPadding = 0; + BYTE* pData = NULL; + + if (pAudioClient == NULL || pRenderClient == NULL) { + return; + } + if (FAILED(pAudioClient->GetCurrentPadding(&nPadding))) { + return; + } + + // leave room for one seg so the first SoundCheck can run a frame immediately + INT32 nFill = (INT32)nTargetFrames - (INT32)nAudSegLen - (INT32)nPadding; + if (nFill > 0 && SUCCEEDED(pRenderClient->GetBuffer(nFill, &pData))) { + pRenderClient->ReleaseBuffer(nFill, AUDCLNT_BUFFERFLAGS_SILENT); + } +} + +static int WasapiStartStream() +{ + if (pAudioClient == NULL) { + return 1; + } + + WasapiPrefill(); + + if (FAILED(pAudioClient->Start())) { + return 1; + } + bStreamStarted = true; + + return 0; +} + +// Default device changed / unplugged etc. Try to reopen on the (new) default device. +static int WasapiReopen() +{ + WasapiReleaseStream(); + ResetEvent(hWasapiEvent); + + if (WasapiOpenStream()) { + return 1; + } + + if (bAudPlaying) { + if (WasapiStartStream()) { + WasapiReleaseStream(); + return 1; + } + } + + return 0; +} + +static void WasapiDeviceLost() +{ +#ifdef PRINT_DEBUG_INFO + bprintf(0, _T("*** WASAPI: audio device lost, will try to reopen.\n")); +#endif + WasapiReleaseStream(); + bDeviceLost = true; + nLastReopenTry = timeGetTime(); + nLostLastFrame = nLastReopenTry; +} + +// Plugin interface + +static int WasapiBlankSound() +{ + if (nAudNextSound) { + AudWriteSilence(); + } + + // Throw away whatever is queued in the device buffer and refill with silence + if (pAudioClient && pRenderClient) { + bBlanked = true; + if (bStreamStarted) { + pAudioClient->Stop(); + pAudioClient->Reset(); + WasapiPrefill(); + pAudioClient->Start(); + } else { + pAudioClient->Reset(); + } + } + + return 0; +} + +// Write the seg in nAudNextSound to the device +static HRESULT WasapiWriteSeg() +{ + BYTE* pData = NULL; + HRESULT hr = pRenderClient->GetBuffer(nAudSegLen, &pData); + if (FAILED(hr)) { + return hr; + } + + WasapiConvert(pData, nAudNextSound, nAudSegLen); + + return pRenderClient->ReleaseBuffer(nAudSegLen, 0); +} + +// Called continuously by the run loop: blocks until there's room for at least one +// seg of sound, then runs the emulation for each free seg (drawing only the last one, +// unless bAlwaysDrawFrames) - same pacing model as the DirectSound plugin. +static int WasapiSoundCheck() +{ + if (nAudNextSound == NULL) { + return 1; + } + + if (bDeviceLost) { + DWORD nNow = timeGetTime(); + + if (nNow - nLastReopenTry >= 1000) { + nLastReopenTry = nNow; + if (WasapiReopen() == 0) { + bDeviceLost = false; +#ifdef PRINT_DEBUG_INFO + bprintf(0, _T("*** WASAPI: audio device reopened.\n")); +#endif + return 0; + } + } + + // No device: keep the emulation running at roughly the right speed, without sound + DWORD nFrameMs = 100000 / (nWasapiFps ? nWasapiFps : 6000); + if (nNow - nLostLastFrame < nFrameMs) { + Sleep(1); + return 0; + } + nLostLastFrame += nFrameMs; + if (nNow - nLostLastFrame > nFrameMs * 4) nLostLastFrame = nNow; // don't try to catch up forever + + WasapiGetNextSound(true); + return 0; + } + + if (pAudioClient == NULL || pRenderClient == NULL) { + return 1; + } + + if (!bAudPlaying) { + return 1; + } + + UINT32 nPadding = 0; + HRESULT hr = pAudioClient->GetCurrentPadding(&nPadding); + if (FAILED(hr)) { + if (hr == AUDCLNT_E_DEVICE_INVALIDATED || hr == AUDCLNT_E_RESOURCES_INVALIDATED) { + WasapiDeviceLost(); + } + return 1; + } + + INT32 nFree = (INT32)nTargetFrames - (INT32)nPadding; + + if (nFree < nAudSegLen) { + // Buffer is full enough - wait for the device to consume some + if (bEventDriven) { + // Event fires once per device period (~10ms); bail out after a while so the UI stays responsive + DWORD nWait = WaitForSingleObject(hWasapiEvent, 200); +#ifdef PRINT_DEBUG_INFO + if (nWait == WAIT_TIMEOUT) { + bprintf(0, _T("*** WASAPI playback notification timeout.\n")); + } +#else + (void)nWait; +#endif + } else { + Sleep(1); + } + return 0; + } + + INT32 nSegs = nFree / nAudSegLen; + + bBlanked = false; + + for (INT32 i = 0; i < nSegs; i++) { + // get more sound into nAudNextSound + WasapiGetNextSound((i == nSegs - 1) || bAlwaysDrawFrames); // If this is the last seg of sound, draw the graphics (frameskipping) + + if (nAudNextSound == NULL || pRenderClient == NULL) { + // sound was shut down from inside the callback + return 1; + } + + if (nAudDSPModule[WASAPI_PLUGIN] & 1) { + if (bRunPause) + AudWriteSilence(); + else + DspDo(nAudNextSound, nAudSegLen); + } + + hr = WasapiWriteSeg(); + if (FAILED(hr)) { + if (hr == AUDCLNT_E_DEVICE_INVALIDATED || hr == AUDCLNT_E_RESOURCES_INVALIDATED) { + WasapiDeviceLost(); + } + // AUDCLNT_E_BUFFER_TOO_LARGE: the callback blanked/refilled the buffer, just drop this seg + return 0; + } + + if (bBlanked) { + // buffer was flushed & re-primed from inside the callback (frame step etc.), start over + bBlanked = false; + break; + } + } + + return 0; +} + +static int WasapiExit() +{ + DspExit(); + + WasapiReleaseStream(); + + if (hWasapiEvent) { + CloseHandle(hWasapiEvent); + hWasapiEvent = NULL; + } + + if (nAudNextSound) { + free(nAudNextSound); + nAudNextSound = NULL; + } + + WasapiGetNextSound = NULL; + + bDeviceLost = false; + nBufferFrames = 0; + nTargetFrames = 0; + szDeviceName[0] = 0; + + return 0; +} + +static int WasapiInit() +{ + if (nAudSampleRate[WASAPI_PLUGIN] <= 0) { + return 1; + } + + if (FAILED(nCOMInit)) { + return 1; + } + + nWasapiFps = nAppVirtualFps; + + // Calculate the Seg Length (round to nearest sample) + nAudSegLen = (nAudSampleRate[WASAPI_PLUGIN] * 100 + (nWasapiFps >> 1)) / nWasapiFps; + nAudAllocSegLen = nAudSegLen << 2; + + bDeviceLost = false; + bStreamStarted = false; + + hWasapiEvent = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset + if (hWasapiEvent == NULL) { + return 1; + } + + if (WasapiOpenStream()) { + WasapiExit(); + return 1; + } + + // Note: +2 is a hacky work-around for crashy Midway games (mk2, etc) via Kaillera (same as DirectSound) + nAudNextSound = (INT16*)malloc(nAudSegLen << (2 + 2)); // The next sound block to put in the stream + if (nAudNextSound == NULL) { + WasapiExit(); + return 1; + } + AudWriteSilence(); + + WasapiSetCallback(NULL); + + DspInit(); + + return 0; +} + +static int WasapiPlay() +{ + if (bDeviceLost) { + // will be started when the device comes back + WasapiSetVolume(); + return 0; + } + + if (pAudioClient == NULL) { + return 1; + } + + if (nAudNextSound) { + AudWriteSilence(); + } + + WasapiSetVolume(); + + if (bStreamStarted) { + pAudioClient->Stop(); + bStreamStarted = false; + } + pAudioClient->Reset(); + + if (WasapiStartStream()) { + return 1; + } + + return 0; +} + +static int WasapiStop() +{ + if (!bAudOkay) { + return 1; + } + + if (pAudioClient && bStreamStarted) { + pAudioClient->Stop(); + pAudioClient->Reset(); + } + bStreamStarted = false; + + return 0; +} + +static int WasapiSetVolume() +{ + // Same curve as the XAudio2 plugin, volume > 100% is allowed (samples are clipped) + if (nAudVolume == 10000) { + fWasapiVol = 1.0f; + } else if (nAudVolume <= 0) { + fWasapiVol = 0.0f; + } else { + fWasapiVol = (float)(((double)nAudVolume / 10000) * ((double)nAudVolume / 10000)); + } + + return 1; // 1 succeeds here (as XAudio2). +} + +static int WasapiGetSettings(InterfaceInfo* pInfo) +{ + TCHAR szString[MAX_PATH] = _T(""); + + if (bDeviceLost) { + _sntprintf(szString, MAX_PATH, _T("Audio device lost, waiting for it to return")); + IntInfoAddStringModule(pInfo, szString); + return 0; + } + + if (szDeviceName[0]) { + _sntprintf(szString, MAX_PATH, _T("Device: %s"), szDeviceName); + IntInfoAddStringModule(pInfo, szString); + } + + _sntprintf(szString, MAX_PATH, _T("Shared mode, %s, %s"), + bEventDriven ? _T("event driven") : _T("polled"), + bAutoConvert ? _T("converted by Windows") : _T("converted to device mix format")); + IntInfoAddStringModule(pInfo, szString); + + if (nAudSampleRate[WASAPI_PLUGIN] > 0) { + _sntprintf(szString, MAX_PATH, _T("Audio is delayed by approx. %ims (device buffer %ims)"), + (int)((INT64)nTargetFrames * 1000 / nAudSampleRate[WASAPI_PLUGIN]), + (int)((INT64)nBufferFrames * 1000 / nAudSampleRate[WASAPI_PLUGIN])); + IntInfoAddStringModule(pInfo, szString); + } + + return 0; +} + +struct AudOut AudOutWASAPI = { WasapiBlankSound, WasapiSoundCheck, WasapiInit, WasapiSetCallback, WasapiPlay, WasapiStop, WasapiExit, WasapiSetVolume, WasapiGetSettings, _T("WASAPI audio output") }; From 105a5f15e09f7f1d1f9b6fd2b370bc7432a2bba8 Mon Sep 17 00:00:00 2001 From: dinkc64 Date: Tue, 27 Oct 2026 02:32:34 -0400 Subject: [PATCH 15/26] speccy update from gab75 --- src/burn/drv/spectrum/d_spectrum.cpp | 294 ++++++++++++++++++++++++--- 1 file changed, 261 insertions(+), 33 deletions(-) diff --git a/src/burn/drv/spectrum/d_spectrum.cpp b/src/burn/drv/spectrum/d_spectrum.cpp index 95aaa4470..893ffd044 100644 --- a/src/burn/drv/spectrum/d_spectrum.cpp +++ b/src/burn/drv/spectrum/d_spectrum.cpp @@ -3502,7 +3502,7 @@ struct BurnDriver BurnSpecbarbarn2 = { // Barmy Burgers (48K) static struct BurnRomInfo SpecBarmyburgRomDesc[] = { - { "Barmy Burgers 48K (1983)(Blaby Computer Games).z80", 28248, 0x138faacd, BRF_ESS | BRF_PRG }, + { "Barmy Burgers 48K (1983)(Blaby Computer Games).tzx", 31126, 0x70455c9d, BRF_ESS | BRF_PRG }, }; STDROMPICKEXT(SpecBarmyburg, SpecBarmyburg, Spectrum) @@ -6121,6 +6121,25 @@ struct BurnDriver BurnSpecCasanova = { &SpecRecalc, 0x10, 288, 224, 4, 3 }; +// Casey Jones (48K) + +static struct BurnRomInfo SpecCaseyjonesRomDesc[] = { + { "Casey Jones 48K (1984)(Blaby Computer Games).tzx", 36421, 0xde4b4598, BRF_ESS | BRF_PRG }, +}; + +STDROMPICKEXT(SpecCaseyjones, SpecCaseyjones, Spectrum) +STD_ROM_FN(SpecCaseyjones) + +struct BurnDriver BurnSpecCaseyjones = { + "spec_caseyjones", NULL, "spec_spectrum", NULL, "1984", + "Casey Jones (48K)\0", NULL, "Blaby Computer Games", "ZX Spectrum", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING, 1, HARDWARE_SPECTRUM, GBF_ACTION | GBF_HORSHOOT, 0, + SpectrumGetZipName, SpecCaseyjonesRomInfo, SpecCaseyjonesRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecDIPInfo, + SpecInit, SpecExit, SpecFrame, SpecDraw, SpecScan, + &SpecRecalc, 0x10, 288, 224, 4, 3 +}; + // Cashdash (48K) static struct BurnRomInfo SpeccashdashRomDesc[] = { @@ -19594,6 +19613,25 @@ struct BurnDriver BurnSpecMindtrap = { &SpecRecalc, 0x10, 288, 224, 4, 3 }; +// Mine Alert (48K) + +static struct BurnRomInfo SpecMinealertRomDesc[] = { + { "Mine Alert 48K (1987)(Ramon Gallastegui).tzx", 31444, 0x8d6e6afa, BRF_ESS | BRF_PRG }, +}; + +STDROMPICKEXT(SpecMinealert, SpecMinealert, Spectrum) +STD_ROM_FN(SpecMinealert) + +struct BurnDriver BurnSpecMinealert = { + "spec_minealert", NULL, "spec_spectrum", NULL, "1987", + "Mine Alert (48K)\0", NULL, "Ramon Gallastegui", "ZX Spectrum", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING, 1, HARDWARE_SPECTRUM, GBF_MAZE, 0, + SpectrumGetZipName, SpecMinealertRomInfo, SpecMinealertRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecQAOPSpaceDIPInfo, + SpecInit, SpecExit, SpecFrame, SpecDraw, SpecScan, + &SpecRecalc, 0x10, 288, 224, 4, 3 +}; + // Mined Out (48K) static struct BurnRomInfo SpecMinedoutRomDesc[] = { @@ -19911,7 +19949,7 @@ struct BurnDriver BurnSpecMoonpatrol = { "spec_moonpatrol", NULL, "spec_spectrum", NULL, "1984", "Moon Patrol (48K)\0", NULL, "Atarisoft", "ZX Spectrum", NULL, NULL, NULL, NULL, - BDF_GAME_WORKING, 2, HARDWARE_SPECTRUM, GBF_ACTION, 0, + BDF_GAME_WORKING, 2, HARDWARE_SPECTRUM, GBF_ACTION | GBF_HORSHOOT, 0, SpectrumGetZipName, SpecMoonpatrolRomInfo, SpecMoonpatrolRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecDIPInfo, SpecInit, SpecExit, SpecFrame, SpecDraw, SpecScan, &SpecRecalc, 0x10, 288, 224, 4, 3 @@ -21380,6 +21418,25 @@ struct BurnDriver BurnSpecNubowls = { &SpecRecalc, 0x10, 288, 224, 4, 3 }; +// Nuke Lear (48K) + +static struct BurnRomInfo SpecNukelearRomDesc[] = { + { "Nuke Lear 48K (1984)(CCS).tzx", 30334, 0xf3e65284, BRF_ESS | BRF_PRG }, +}; + +STDROMPICKEXT(SpecNukelear, SpecNukelear, Spectrum) +STD_ROM_FN(SpecNukelear) + +struct BurnDriver BurnSpecNukelear = { + "spec_nukelear", NULL, "spec_spectrum", NULL, "1984", + "Nuke Lear (48K)\0", NULL, "CCS", "ZX Spectrum", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING, 1, HARDWARE_SPECTRUM, GBF_ACTION, 0, + SpectrumGetZipName, SpecNukelearRomInfo, SpecNukelearRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecDIPInfo, + SpecInit, SpecExit, SpecFrame, SpecDraw, SpecScan, + &SpecRecalc, 0x10, 288, 224, 4, 3 +}; + // Number Painter (48K) static struct BurnRomInfo SpecNumbpaintRomDesc[] = { @@ -32248,6 +32305,44 @@ struct BurnDriver BurnSpecViz = { &SpecRecalc, 0x10, 288, 224, 4, 3 }; +// Vulcan ~ The Tunisian Campaign (128K) + +static struct BurnRomInfo SpecVulcan128RomDesc[] = { + { "Vulcan 128K (1986)(CCS).tzx", 53740, 0xc1299a09, BRF_ESS | BRF_PRG }, +}; + +STDROMPICKEXT(SpecVulcan128, SpecVulcan128, Spec128) +STD_ROM_FN(SpecVulcan128) + +struct BurnDriver BurnSpecVulcan128 = { + "spec_vulcan128", NULL, "spec_spec128", NULL, "1986", + "Vulcan ~ The Tunisian Campaign (128K)\0", NULL, "CCS", "ZX Spectrum", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING, 2, HARDWARE_SPECTRUM, GBF_STRATEGY, 0, + SpectrumGetZipName, SpecVulcan128RomInfo, SpecVulcan128RomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecCursorKeysDIPInfo, + Spec128KInit, SpecExit, SpecFrame, SpecDraw, SpecScan, + &SpecRecalc, 0x10, 288, 224, 4, 3 +}; + +// Vulcan ~ The Tunisian Campaign (48K) + +static struct BurnRomInfo SpecVulcan48RomDesc[] = { + { "Vulcan 48K (1986)(CCS).tzx", 49796, 0x378fc601, BRF_ESS | BRF_PRG }, +}; + +STDROMPICKEXT(SpecVulcan48, SpecVulcan48, Spectrum) +STD_ROM_FN(SpecVulcan48) + +struct BurnDriver BurnSpecVulcan48 = { + "spec_vulcan48", "spec_vulcan128", "spec_spectrum", NULL, "1986", + "Vulcan ~ The Tunisian Campaign (48K)\0", NULL, "CCS", "ZX Spectrum", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE, 2, HARDWARE_SPECTRUM, GBF_STRATEGY, 0, + SpectrumGetZipName, SpecVulcan48RomInfo, SpecVulcan48RomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecCursorKeysDIPInfo, + SpecInit, SpecExit, SpecFrame, SpecDraw, SpecScan, + &SpecRecalc, 0x10, 288, 224, 4, 3 +}; + // Wacky Darts (48K-128K) static struct BurnRomInfo SpecWackydartsRomDesc[] = { @@ -32516,19 +32611,19 @@ struct BurnDriver BurnSpecWhiteheat = { // Who Dares Wins II (48K) -static struct BurnRomInfo SpecWhodareswinsiiRomDesc[] = { - { "Who Dares Wins II 48K (1986)(Alligata).tap", 48631, 0x02dc6931, BRF_ESS | BRF_PRG }, +static struct BurnRomInfo SpecWdwiiRomDesc[] = { + { "Who Dares Wins II 48K (1986)(Alligata).tzx", 48692, 0xbf14f1aa, BRF_ESS | BRF_PRG }, }; -STDROMPICKEXT(SpecWhodareswinsii, SpecWhodareswinsii, Spectrum) -STD_ROM_FN(SpecWhodareswinsii) +STDROMPICKEXT(SpecWdwii, SpecWdwii, Spectrum) +STD_ROM_FN(SpecWdwii) -struct BurnDriver BurnSpecWhodareswinsii = { - "spec_whodareswinsii", NULL, "spec_spectrum", NULL, "1986", +struct BurnDriver BurnSpecWdwii = { + "spec_wdwii", NULL, "spec_spectrum", NULL, "1986", "Who Dares Wins II (48K)\0", NULL, "Alligata Software", "ZX Spectrum", NULL, NULL, NULL, NULL, BDF_GAME_WORKING, 1, HARDWARE_SPECTRUM, GBF_RUNGUN, 0, - SpectrumGetZipName, SpecWhodareswinsiiRomInfo, SpecWhodareswinsiiRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecDIPInfo, + SpectrumGetZipName, SpecWdwiiRomInfo, SpecWdwiiRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecDIPInfo, SpecInit, SpecExit, SpecFrame, SpecDraw, SpecScan, &SpecRecalc, 0x10, 288, 224, 4, 3 }; @@ -52910,6 +53005,25 @@ struct BurnDriver BurnSpecModrains = { &SpecRecalc, 0x10, 288, 224, 4, 3 }; +// Mine Alert 2 (128K) (HB) + +static struct BurnRomInfo SpecMinealert2RomDesc[] = { + { "Mine Alert 2 128K (2026)(namco-nexgen).tap", 85093, 0x2eca386a, BRF_ESS | BRF_PRG }, +}; + +STDROMPICKEXT(SpecMinealert2, SpecMinealert2, Spec128) +STD_ROM_FN(SpecMinealert2) + +struct BurnDriver BurnSpecMinealert2 = { + "spec_minealert2", NULL, "spec_spec128", NULL, "2026", + "Mine Alert 2 (128K) (HB)\0", NULL, "namco/nexgen", "ZX Spectrum", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_SPECTRUM, GBF_MAZE | GBF_MULTISHOOT, 0, + SpectrumGetZipName, SpecMinealert2RomInfo, SpecMinealert2RomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecDIPInfo, + Spec128KInit, SpecExit, SpecFrame, SpecDraw, SpecScan, + &SpecRecalc, 0x10, 288, 224, 4, 3 +}; + // Minesweeper (128K) (HB, v1.03) static struct BurnRomInfo SpecMinesweeper128RomDesc[] = { @@ -53481,10 +53595,10 @@ struct BurnDriver BurnSpecMoonpirates = { &SpecRecalc, 0x10, 288, 224, 4, 3 }; -// Moon Ranger (128K) (HB) +// Moon Ranger (48K-128K) (HB) static struct BurnRomInfo SpecMoonrangerRomDesc[] = { - { "Moon Ranger 128K (2020)(Gabriele Amore).tap", 29940, 0xd33c199a, BRF_ESS | BRF_PRG }, + { "Moon Ranger 48K-128K (2020)(Gabriele Amore).tap", 29940, 0xd33c199a, BRF_ESS | BRF_PRG }, }; STDROMPICKEXT(SpecMoonranger, SpecMoonranger, Spec128) @@ -53492,18 +53606,18 @@ STD_ROM_FN(SpecMoonranger) struct BurnDriver BurnSpecMoonranger = { "spec_moonranger", NULL, "spec_spec128", NULL, "2020", - "Moon Ranger (128K) (HB)\0", NULL, "Gabriele Amore", "ZX Spectrum", + "Moon Ranger (48K-128K) (HB)\0", NULL, "Gabriele Amore", "ZX Spectrum", NULL, NULL, NULL, NULL, - BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_SPECTRUM, GBF_ACTION, 0, + BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_SPECTRUM, GBF_ACTION | GBF_HORSHOOT, 0, SpectrumGetZipName, SpecMoonrangerRomInfo, SpecMoonrangerRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecQAOPMDIPInfo, Spec128KInit, SpecExit, SpecFrame, SpecDraw, SpecScan, &SpecRecalc, 0x10, 288, 224, 4, 3 }; -// Moon Ranger (128K) (HB, v2) +// Moon Ranger (48K-128K) (HB, v2) static struct BurnRomInfo SpecMoonrangerv2RomDesc[] = { - { "Moon Ranger v2 128K (2021)(Gabriele Amore).tap", 45527, 0xebb5c2a1, BRF_ESS | BRF_PRG }, + { "Moon Ranger v2 48K-128K (2021)(Gabriele Amore).tap", 45527, 0xebb5c2a1, BRF_ESS | BRF_PRG }, }; STDROMPICKEXT(SpecMoonrangerv2, SpecMoonrangerv2, Spec128) @@ -53511,9 +53625,9 @@ STD_ROM_FN(SpecMoonrangerv2) struct BurnDriver BurnSpecMoonrangerv2 = { "spec_moonrangerv2", NULL, "spec_spec128", NULL, "2021", - "Moon Ranger (128K) (HB, v2)\0", NULL, "Gabriele Amore", "ZX Spectrum", + "Moon Ranger (48K-128K) (HB, v2)\0", NULL, "Gabriele Amore", "ZX Spectrum", NULL, NULL, NULL, NULL, - BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_SPECTRUM, GBF_ACTION, 0, + BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_SPECTRUM, GBF_ACTION | GBF_HORSHOOT, 0, SpectrumGetZipName, SpecMoonrangerv2RomInfo, SpecMoonrangerv2RomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecQAOPMDIPInfo, Spec128KInit, SpecExit, SpecFrame, SpecDraw, SpecScan, &SpecRecalc, 0x10, 288, 224, 4, 3 @@ -55571,6 +55685,25 @@ struct BurnDriver BurnSpecOure = { &SpecRecalc, 0x10, 288, 224, 4, 3 }; +// Outpost (48K) (HB) + +static struct BurnRomInfo SpecOutpostRomDesc[] = { + { "Outpost 48K (2026)(usebox.net).tap", 22114, 0x2ac59251, BRF_ESS | BRF_PRG }, +}; + +STDROMPICKEXT(SpecOutpost, SpecOutpost, Spectrum) +STD_ROM_FN(SpecOutpost) + +struct BurnDriver BurnSpecOutpost = { + "spec_outpost", NULL, "spec_spectrum", NULL, "2026", + "Outpost (48K) (HB)\0", NULL, "usebox.net", "ZX Spectrum", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_SPECTRUM, GBF_PLATFORM, 0, + SpectrumGetZipName, SpecOutpostRomInfo, SpecOutpostRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecDIPInfo, + SpecInit, SpecExit, SpecFrame, SpecDraw, SpecScan, + &SpecRecalc, 0x10, 288, 224, 4, 3 +}; + // P-47 The Freedom Fighter (128K) (Hack) static struct BurnRomInfo SpecP47freedomfRomDesc[] = { @@ -58801,10 +58934,10 @@ struct BurnDriver BurnSpecRiverrad = { &SpecRecalc, 0x10, 288, 224, 4, 3 }; -// River Raid+ (48K) (Hack, v0.2) +// River Raid+ (48K) (Hack, v0.7) static struct BurnRomInfo SpecRiveraidplusRomDesc[] = { - { "River Raid+ v0.2 48K (2026)(theALFEST).tap", 22554, 0x2e917c63, BRF_ESS | BRF_PRG }, + { "River Raid+ v0.7 48K-AY (2026)(theALFEST).tap", 26652, 0x53f67db2, BRF_ESS | BRF_PRG }, }; STDROMPICKEXT(SpecRiveraidplus, SpecRiveraidplus, Spectrum) @@ -58812,7 +58945,7 @@ STD_ROM_FN(SpecRiveraidplus) struct BurnDriver BurnSpecRiveraidplus = { "spec_riveraidplus", "spec_riveraid", "spec_spectrum", NULL, "2026", - "River Raid+ (48K) (Hack, v0.2)\0", NULL, "theALFEST", "ZX Spectrum", + "River Raid+ (48K) (Hack, v0.7)\0", "AY Sound supported", "theALFEST", "ZX Spectrum", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 1, HARDWARE_SPECTRUM, GBF_VERSHOOT, 0, SpectrumGetZipName, SpecRiveraidplusRomInfo, SpecRiveraidplusRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecDIPInfo, @@ -60568,10 +60701,10 @@ struct BurnDriver BurnSpecShadowfields = { &SpecRecalc, 0x10, 288, 224, 4, 3 }; -// Shadows of Belial (English) (128K) (HB, v1.0.4) +// Shadows of Belial (English) (128K) (HB, v1.0.5) static struct BurnRomInfo SpecShabelialenRomDesc[] = { - { "Shadows of Belial EN v1.0.4 128K (2016)(RetroWorks).tap", 138736, 0xcaf1f445, BRF_ESS | BRF_PRG }, + { "Shadows of Belial EN v1.0.5 128K (2026)(RetroWorks).tap", 138687, 0x4ac79734, BRF_ESS | BRF_PRG }, }; STDROMPICKEXT(SpecShabelialen, SpecShabelialen, Spec128) @@ -60579,7 +60712,7 @@ STD_ROM_FN(SpecShabelialen) struct BurnDriver BurnSpecShabelialen = { "spec_shabelialen", NULL, "spec_spec128", NULL, "RetroWorks", - "Shadows of Belial (English) (128K) (HB, v1.0.4)\0", NULL, "2026", "ZX Spectrum", + "Shadows of Belial (English) (128K) (HB, v1.0.5)\0", NULL, "2026", "ZX Spectrum", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_SPECTRUM, GBF_PLATFORM, 0, SpectrumGetZipName, SpecShabelialenRomInfo, SpecShabelialenRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecDIPInfo, @@ -60587,10 +60720,10 @@ struct BurnDriver BurnSpecShabelialen = { &SpecRecalc, 0x10, 288, 224, 4, 3 }; -// Shadows of Belial (Spanish) (128K) (HB, v1.0.4) +// Shadows of Belial (Spanish) (128K) (HB, v1.0.5) static struct BurnRomInfo SpecShabelialesRomDesc[] = { - { "Shadows of Belial ES v1.0.4 128K (2026)(RetroWorks).tap", 139295, 0x07167c28, BRF_ESS | BRF_PRG }, + { "Shadows of Belial ES v1.0.5 128K (2026)(RetroWorks).tap", 139297, 0x2faa2142, BRF_ESS | BRF_PRG }, }; STDROMPICKEXT(SpecShabeliales, SpecShabeliales, Spec128) @@ -60598,7 +60731,7 @@ STD_ROM_FN(SpecShabeliales) struct BurnDriver BurnSpecShabeliales = { "spec_shabeliales", "spec_shabelialen", "spec_spec128", NULL, "RetroWorks", - "Shadows of Belial (Spanish) (128K) (HB, v1.0.4)\0", NULL, "2026", "ZX Spectrum", + "Shadows of Belial (Spanish) (128K) (HB, v1.0.5)\0", NULL, "2026", "ZX Spectrum", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_CLONE | BDF_HOMEBREW, 1, HARDWARE_SPECTRUM, GBF_PLATFORM, 0, SpectrumGetZipName, SpecShabelialesRomInfo, SpecShabelialesRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecDIPInfo, @@ -60606,6 +60739,44 @@ struct BurnDriver BurnSpecShabeliales = { &SpecRecalc, 0x10, 288, 224, 4, 3 }; +// Shadows of Belial (Portuguese) (128K) (HB, v1.0.5) + +static struct BurnRomInfo SpecShabelialptRomDesc[] = { + { "Shadows of Belial PT v1.0.5 128K (2026)(RetroWorks).tap", 139372, 0xb20c499b, BRF_ESS | BRF_PRG }, +}; + +STDROMPICKEXT(SpecShabelialpt, SpecShabelialpt, Spec128) +STD_ROM_FN(SpecShabelialpt) + +struct BurnDriver BurnSpecShabelialpt = { + "spec_shabelialpt", "spec_shabelialen", "spec_spec128", NULL, "RetroWorks", + "Shadows of Belial (Portuguese) (128K) (HB, v1.0.5)\0", NULL, "2026", "ZX Spectrum", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HOMEBREW, 1, HARDWARE_SPECTRUM, GBF_PLATFORM, 0, + SpectrumGetZipName, SpecShabelialptRomInfo, SpecShabelialptRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecDIPInfo, + Spec128KInit, SpecExit, SpecFrame, SpecDraw, SpecScan, + &SpecRecalc, 0x10, 288, 224, 4, 3 +}; + +// Shadows of Belial (Russian) (128K) (HB, v1.0.5) + +static struct BurnRomInfo SpecShabelialruRomDesc[] = { + { "Shadows of Belial RU v1.0.5 128K (2026)(RetroWorks).tap", 139086, 0x719f3964, BRF_ESS | BRF_PRG }, +}; + +STDROMPICKEXT(SpecShabelialru, SpecShabelialru, Spec128) +STD_ROM_FN(SpecShabelialru) + +struct BurnDriver BurnSpecShabelialru = { + "spec_shabelialru", "spec_shabelialen", "spec_spec128", NULL, "RetroWorks", + "Shadows of Belial (Russian) (128K) (HB, v1.0.5)\0", NULL, "2026", "ZX Spectrum", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HOMEBREW, 1, HARDWARE_SPECTRUM, GBF_PLATFORM, 0, + SpectrumGetZipName, SpecShabelialruRomInfo, SpecShabelialruRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecDIPInfo, + Spec128KInit, SpecExit, SpecFrame, SpecDraw, SpecScan, + &SpecRecalc, 0x10, 288, 224, 4, 3 +}; + // Shape Shifter (48K) (HB) static struct BurnRomInfo SpecShapeshiftRomDesc[] = { @@ -66382,6 +66553,25 @@ struct BurnDriver BurnSpecWhitejaguarzx = { &SpecRecalc, 0x10, 288, 224, 4, 3 }; +// Who Dares Wins III (128K) (HB, v1.1) + +static struct BurnRomInfo SpecWdwiiiRomDesc[] = { + { "Who Dares Wins III v1.1 128K (2026)(namco-nexgen).tap", 73967, 0xb8f9cc8b, BRF_ESS | BRF_PRG }, +}; + +STDROMPICKEXT(SpecWdwiii, SpecWdwiii, Spec128) +STD_ROM_FN(SpecWdwiii) + +struct BurnDriver BurnSpecWdwiii = { + "spec_wdwiii", NULL, "spec_spec128", NULL, "2026", + "Who Dares Wins III (128K) (HB, v1.1)\0", NULL, "namco/nexgen", "ZX Spectrum", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_SPECTRUM, GBF_RUNGUN, 0, + SpectrumGetZipName, SpecWdwiiiRomInfo, SpecWdwiiiRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecDIPInfo, + Spec128KInit, SpecExit, SpecFrame, SpecDraw, SpecScan, + &SpecRecalc, 0x10, 288, 224, 4, 3 +}; + // Wiking - Improved Version (Polish) (128K) (Hack) static struct BurnRomInfo SpecWiking128RomDesc[] = { @@ -66858,6 +67048,25 @@ struct BurnDriver BurnSpecXenomaze = { &SpecRecalc, 0x10, 288, 224, 4, 3 }; +// XPong48 (48K) (HB) + +static struct BurnRomInfo SpecXpong48RomDesc[] = { + { "XPong48 48K-AY (2026)(CodeBiters).tap", 32548, 0xc563401a, BRF_ESS | BRF_PRG }, +}; + +STDROMPICKEXT(SpecXpong48, SpecXpong48, Spectrum) +STD_ROM_FN(SpecXpong48) + +struct BurnDriver BurnSpecXpong48 = { + "spec_xpong48", NULL, "spec_spectrum", NULL, "2026", + "XPong48 (48K) (HB)\0", "AY Sound supported", "CodeBiters", "ZX Spectrum", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_HOMEBREW, 2, HARDWARE_SPECTRUM, GBF_BALLPADDLE, 0, + SpectrumGetZipName, SpecXpong48RomInfo, SpecXpong48RomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecDIPInfo, + SpecInit, SpecExit, SpecFrame, SpecDraw, SpecScan, + &SpecRecalc, 0x10, 288, 224, 4, 3 +}; + // X=Y=Z (48K) (HB) static struct BurnRomInfo SpecXyzRomDesc[] = { @@ -67428,21 +67637,40 @@ struct BurnDriver BurnSpecZlalox = { &SpecRecalc, 0x10, 288, 224, 4, 3 }; -// Zoinho (Spanish) (48K) (HB) +// Zoinho: No Jardim dos Tolos (Spanish) (48K) (HB) static struct BurnRomInfo SpecZoinhoRomDesc[] = { - { "Zoinho 48K (2021)(Bitnamic).tap", 29137, 0xa64d5e5a, BRF_ESS | BRF_PRG }, + { "Zoinho - No Jardim dos Tolos 48K (2021)(Bitnamic).tap", 29137, 0xa64d5e5a, BRF_ESS | BRF_PRG }, }; STDROMPICKEXT(SpecZoinho, SpecZoinho, Spectrum) STD_ROM_FN(SpecZoinho) struct BurnDriver BurnSpecZoinho = { - "spec_zoinho", NULL, "spec_spectrum", NULL, "2021", - "Zoinho (Spanish) (48K) (HB)\0", NULL, "Bitnamic", "ZX Spectrum", + "spec_zoinho", "spec_zoinho25", "spec_spectrum", NULL, "2021", + "Zoinho: No Jardim dos Tolos (Spanish) (48K) (HB)\0", NULL, "Bitnamic", "ZX Spectrum", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HOMEBREW, 1, HARDWARE_SPECTRUM, GBF_ACTION | GBF_ADV, 0, + SpectrumGetZipName, SpecZoinhoRomInfo, SpecZoinhoRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecIntf2DIPInfo, + SpecInit, SpecExit, SpecFrame, SpecDraw, SpecScan, + &SpecRecalc, 0x10, 288, 224, 4, 3 +}; + +// Zoinho: In the Garden of the Fools (English) (48K) (HB) + +static struct BurnRomInfo SpecZoinho25RomDesc[] = { + { "Zoinho - In the Garden of the Fools 48K (2025)(R.C. Nunes).tap", 42058, 0x7ec6faaa, BRF_ESS | BRF_PRG }, +}; + +STDROMPICKEXT(SpecZoinho25, SpecZoinho25, Spectrum) +STD_ROM_FN(SpecZoinho25) + +struct BurnDriver BurnSpecZoinho25 = { + "spec_zoinho25", NULL, "spec_spectrum", NULL, "2025", + "Zoinho: In the Garden of the Fools (English) (48K) (HB)\0", "Set 'Cursor Keys' to use controller", "R.C. Nunes", "ZX Spectrum", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_SPECTRUM, GBF_ACTION | GBF_ADV, 0, - SpectrumGetZipName, SpecZoinhoRomInfo, SpecZoinhoRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecIntf2DIPInfo, + SpectrumGetZipName, SpecZoinho25RomInfo, SpecZoinho25RomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecCursorKeysDIPInfo, SpecInit, SpecExit, SpecFrame, SpecDraw, SpecScan, &SpecRecalc, 0x10, 288, 224, 4, 3 }; @@ -67450,7 +67678,7 @@ struct BurnDriver BurnSpecZoinho = { // Zombie Calavera Prologue (48K) (HB) static struct BurnRomInfo SpeczcalaveraRomDesc[] = { - { "Zombie Calavera Prologue 48K (2010)(Mojon Twins).tap", 37966, 0x9eaf2b79, BRF_ESS | BRF_PRG }, + { "Zombie Calavera Prologue 48K (2010)(The Mojon Twins).tzx", 38000, 0xbb2c916b, BRF_ESS | BRF_PRG }, }; STDROMPICKEXT(Speczcalavera, Speczcalavera, Spectrum) @@ -67460,7 +67688,7 @@ struct BurnDriver BurnSpeczcalavera = { "spec_zcalavera", NULL, "spec_spectrum", NULL, "2010", "Zombie Calavera Prologue (48K) (HB)\0", NULL, "The Mojon Twins", "ZX Spectrum", NULL, NULL, NULL, NULL, - BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_SPECTRUM, GBF_RUNGUN | GBF_PLATFORM, 0, + BDF_GAME_WORKING | BDF_HOMEBREW, 1, HARDWARE_SPECTRUM, GBF_PLATFORM | GBF_RUNGUN, 0, SpectrumGetZipName, SpeczcalaveraRomInfo, SpeczcalaveraRomName, NULL, NULL, NULL, NULL, SpecInputInfo, SpecDIPInfo, SpecInit, SpecExit, SpecFrame, SpecDraw, SpecScan, &SpecRecalc, 0x10, 288, 224, 4, 3 From 40a72a681ea8e10069ab7e0c5e6c40cf45caa060 Mon Sep 17 00:00:00 2001 From: dinkc64 Date: Tue, 27 Oct 2026 10:50:36 -0400 Subject: [PATCH 16/26] d_williams, update w/williams2 games --- src/burn/drv/pre90s/d_williams.cpp | 1523 ++++++++++++++++++++++++++-- 1 file changed, 1433 insertions(+), 90 deletions(-) diff --git a/src/burn/drv/pre90s/d_williams.cpp b/src/burn/drv/pre90s/d_williams.cpp index 058e5b4c7..8050f878b 100644 --- a/src/burn/drv/pre90s/d_williams.cpp +++ b/src/burn/drv/pre90s/d_williams.cpp @@ -17,20 +17,22 @@ // Blaster // Speed Ball // Lottofun - -// Not added yet: -// mysticm, mysticmp -// tshoot -// inferno -// joust2, joust2r1 +// Inferno +// Mystic Marathon +// Turkey Shoot +// Joust 2 #include "tiles_generic.h" #include "m6809_intf.h" #include "m6800_intf.h" #include "dac.h" #include "hc55516.h" +#include "midcvsd.h" +#include "burn_ym2151.h" +#include "timer.h" #include "watchdog.h" #include "6821pia.h" +#include "burn_gun.h" static UINT8 *AllMem; static UINT8 *MemEnd; @@ -41,20 +43,50 @@ static UINT8 *DrvM6800ROM0; static UINT8 *DrvM6800ROM1; static UINT8 *DrvGfxROM; static UINT8 *DrvColPROM; +static UINT8 *DrvTileROM; +static UINT8 *DrvBgROM; static UINT8 *DrvNVRAM; static UINT8 *DrvM6809RAM0; static UINT8 *DrvM6800RAM0; static UINT8 *DrvM6800RAM1; static UINT8 *DrvVidRAM; static UINT8 *DrvPalRAM; +static UINT8 *DrvTileRAM; +static UINT8 *DrvBgRAM; static UINT8 *DrvBlitRAM; // 8 bytes +static UINT8 *DrvPalWritten; static UINT8 *blitter_remap; // 256*256 static UINT32 *Palette; static UINT32 *DrvPalette; static UINT8 DrvRecalc; +// williams 2 config +static INT32 williams2 = 0; +static INT32 is_joust2 = 0; +static INT32 is_tshoot = 0; +static INT32 is_mysticm = 0; +static INT32 has_s11_bg = 0; // joust2 bg sound board +static INT32 w2_bg_color = 0; +static INT32 has_second_soundboard = 0; +static INT32 is_blasterkit = 0; +static INT32 w2_fg_color = 0; +static INT32 w2_bg_palette_offset = 0; +static INT32 w2_xscroll = 0; +static INT32 w2_numtiles = 0; +static INT32 w2_rom_view_active = 0; +static INT32 w2_palette_view_active = 0; +static INT32 w2_bank_entry = 0; +static UINT8 w2_bank_select_shadow = 0; +static UINT8 w2_fg_select_shadow = 0; +static UINT8 w2_bg_select_shadow = 0; +static UINT8 w2_xscroll_low_shadow = 0; +static UINT8 w2_xscroll_high_shadow = 0; +static UINT8 w2_video_control_shadow = 0; +static UINT8 w2_window_enable_shadow = 0; +static UINT8 w2_watchdog_shadow = 0; + static INT32 nExtraCycles[3]; static UINT8 blitter_xor = 0; @@ -92,6 +124,7 @@ static UINT8 lotto_ticket; static UINT8 lotto_motor; static INT32 TrackX[2] = { 0, 0 }; static INT32 TrackY[2] = { 0, 0 }; +static INT32 conquest_dial = 0; static ButtonToggle MemoryProtect; // lottofun @@ -104,6 +137,7 @@ static INT32 sinistarc = 0; static INT32 playball = 0; static INT32 spdball = 0; static INT32 lottofun = 0; +static INT32 conquest = 0; static INT32 uses_hc55516 = 0; static INT32 uses_colprom = 0; @@ -228,6 +262,125 @@ static struct BurnInputInfo StargateInputList[] = { STDINPUTINFO(Stargate) +#define A(a, b, c, d) {a, b, (UINT8*)(c), d} +static struct BurnInputInfo ConquestInputList[] = { + {"P1 Start", BIT_DIGITAL, DrvJoy1 + 5, "p1 start" }, + {"Fire", BIT_DIGITAL, DrvJoy1 + 0, "p1 fire 1" }, + {"Thrust", BIT_DIGITAL, DrvJoy1 + 1, "p1 fire 2" }, + + {"P2 Start", BIT_DIGITAL, DrvJoy1 + 4, "p2 start" }, + + A("Dial", BIT_ANALOG_REL, &DrvAnalogPort0, "p1 dial"), + + {"Reset", BIT_DIGITAL, &DrvReset, "reset" }, +}; +#undef A + +STDINPUTINFO(Conquest) + +static struct BurnInputInfo InfernoInputList[] = { + {"P1 Start", BIT_DIGITAL, DrvJoy1 + 6, "p1 start" }, + {"P1 Button 1", BIT_DIGITAL, DrvJoy1 + 0, "p1 fire 1" }, + {"P1 Up", BIT_DIGITAL, DrvJoy4 + 0, "p1 up (left stick)" }, + {"P1 Down", BIT_DIGITAL, DrvJoy4 + 3, "p1 down (left stick)" }, + {"P1 Left", BIT_DIGITAL, DrvJoy4 + 1, "p1 left (left stick)" }, + {"P1 Right", BIT_DIGITAL, DrvJoy4 + 2, "p1 right (left stick)" }, + {"P1 Up 2", BIT_DIGITAL, DrvJoy4 + 4, "p1 up (right stick)" }, + {"P1 Down 2", BIT_DIGITAL, DrvJoy4 + 7, "p1 down (right stick)" }, + {"P1 Left 2", BIT_DIGITAL, DrvJoy4 + 5, "p1 left (right stick)" }, + {"P1 Right 2", BIT_DIGITAL, DrvJoy4 + 6, "p1 right (right stick)" }, + + {"P2 Start", BIT_DIGITAL, DrvJoy1 + 7, "p2 start" }, + {"P2 Button 1", BIT_DIGITAL, DrvJoy1 + 1, "p2 fire 1" }, + {"P2 Up", BIT_DIGITAL, DrvJoy5 + 0, "p2 up (left stick)" }, + {"P2 Down", BIT_DIGITAL, DrvJoy5 + 3, "p2 down (left stick)" }, + {"P2 Left", BIT_DIGITAL, DrvJoy5 + 1, "p2 left (left stick)" }, + {"P2 Right", BIT_DIGITAL, DrvJoy5 + 2, "p2 right (left stick)" }, + {"P2 Up 2", BIT_DIGITAL, DrvJoy5 + 4, "p2 up (right stick)" }, + {"P2 Down 2", BIT_DIGITAL, DrvJoy5 + 7, "p2 down (right stick)" }, + {"P2 Left 2", BIT_DIGITAL, DrvJoy5 + 5, "p2 left (right stick)" }, + {"P2 Right 2", BIT_DIGITAL, DrvJoy5 + 6, "p2 right (right stick)" }, + + {"Reset", BIT_DIGITAL, &DrvReset, "reset" }, + {"Auto Up / Manual Down", BIT_DIGITAL, DrvJoy2 + 0, "service" }, + {"Advance", BIT_DIGITAL, DrvJoy2 + 1, "diag" }, + {"High Score Reset", BIT_DIGITAL, DrvJoy2 + 2, "service3" }, + {"P1 Coin", BIT_DIGITAL, DrvJoy2 + 3, "p1 coin" }, + {"P2 Coin", BIT_DIGITAL, DrvJoy2 + 4, "p2 coin" }, + {"P3 Coin", BIT_DIGITAL, DrvJoy2 + 5, "p3 coin" }, + {"Tilt", BIT_DIGITAL, DrvJoy2 + 6, "tilt" }, +}; + +STDINPUTINFO(Inferno) + +static struct BurnInputInfo Joust2InputList[] = { + {"P1 Start/Transform", BIT_DIGITAL, DrvJoy1 + 5, "p1 start" }, + {"P1 Left", BIT_DIGITAL, DrvJoy4 + 0, "p1 left" }, + {"P1 Right", BIT_DIGITAL, DrvJoy4 + 1, "p1 right" }, + {"P1 Flap", BIT_DIGITAL, DrvJoy4 + 2, "p1 fire 1" }, + + {"P2 Start/Transform", BIT_DIGITAL, DrvJoy1 + 4, "p2 start" }, + {"P2 Left", BIT_DIGITAL, DrvJoy5 + 0, "p2 left" }, + {"P2 Right", BIT_DIGITAL, DrvJoy5 + 1, "p2 right" }, + {"P2 Flap", BIT_DIGITAL, DrvJoy5 + 2, "p2 fire 1" }, + + {"Reset", BIT_DIGITAL, &DrvReset, "reset" }, + {"Auto Up / Manual Down", BIT_DIGITAL, DrvJoy3 + 0, "service" }, + {"Advance", BIT_DIGITAL, DrvJoy3 + 1, "diag" }, + {"High Score Reset", BIT_DIGITAL, DrvJoy3 + 2, "service3" }, + {"P1 Coin", BIT_DIGITAL, DrvJoy3 + 3, "p1 coin" }, + {"P2 Coin", BIT_DIGITAL, DrvJoy3 + 4, "p2 coin" }, + {"Coin 3", BIT_DIGITAL, DrvJoy3 + 5, "service2" }, + {"Tilt", BIT_DIGITAL, DrvJoy3 + 6, "tilt" }, +}; + +STDINPUTINFO(Joust2) + +#define A(a, b, c, d) {a, b, (UINT8*)(c), d} +static struct BurnInputInfo TshootInputList[] = { + {"Start 1", BIT_DIGITAL, DrvJoy2 + 6, "p1 start" }, + {"Start 2", BIT_DIGITAL, DrvJoy2 + 7, "p2 start" }, + {"Grenade", BIT_DIGITAL, DrvJoy2 + 0, "p1 fire 2" }, + {"Gobble", BIT_DIGITAL, DrvJoy2 + 1, "p1 fire 3" }, + {"Fire", BIT_DIGITAL, DrvJoy4 + 7, "p1 fire 1" }, + + A("Gun X", BIT_ANALOG_REL, &DrvAnalogPort0,"p1 x-axis"), + A("Gun Y", BIT_ANALOG_REL, &DrvAnalogPort1,"p1 y-axis"), + + {"Reset", BIT_DIGITAL, &DrvReset, "reset" }, + {"Auto Up / Manual Down", BIT_DIGITAL, DrvJoy3 + 0, "service" }, + {"Advance", BIT_DIGITAL, DrvJoy3 + 1, "diag" }, + {"High Score Reset", BIT_DIGITAL, DrvJoy3 + 2, "service3" }, + {"P1 Coin", BIT_DIGITAL, DrvJoy3 + 3, "p1 coin" }, + {"P2 Coin", BIT_DIGITAL, DrvJoy3 + 4, "p2 coin" }, + {"Coin 3", BIT_DIGITAL, DrvJoy3 + 5, "service2" }, + {"Tilt", BIT_DIGITAL, DrvJoy3 + 6, "tilt" }, +}; +#undef A + +STDINPUTINFO(Tshoot) + +static struct BurnInputInfo MysticmInputList[] = { + {"P1 Up", BIT_DIGITAL, DrvJoy1 + 0, "p1 up" }, + {"P1 Right", BIT_DIGITAL, DrvJoy1 + 1, "p1 right" }, + {"P1 Down", BIT_DIGITAL, DrvJoy1 + 2, "p1 down" }, + {"P1 Left", BIT_DIGITAL, DrvJoy1 + 3, "p1 left" }, + {"P1 Start", BIT_DIGITAL, DrvJoy1 + 4, "p1 start" }, + {"P2 Start", BIT_DIGITAL, DrvJoy1 + 5, "p2 start" }, + {"P1 Button 1", BIT_DIGITAL, DrvJoy1 + 7, "p1 fire 1" }, + + {"Reset", BIT_DIGITAL, &DrvReset, "reset" }, + {"Auto Up / Manual Down", BIT_DIGITAL, DrvJoy3 + 0, "service" }, + {"Advance", BIT_DIGITAL, DrvJoy3 + 1, "diag" }, + {"High Score Reset", BIT_DIGITAL, DrvJoy3 + 2, "service3" }, + {"P1 Coin", BIT_DIGITAL, DrvJoy3 + 3, "p1 coin" }, + {"P2 Coin", BIT_DIGITAL, DrvJoy3 + 4, "p2 coin" }, + {"Coin 3", BIT_DIGITAL, DrvJoy3 + 5, "service2" }, + {"Tilt", BIT_DIGITAL, DrvJoy3 + 6, "tilt" }, +}; + +STDINPUTINFO(Mysticm) + static struct BurnInputInfo RobotronInputList[] = { {"P1 Coin", BIT_DIGITAL, DrvJoy3 + 4, "p1 coin" }, {"P1 Start", BIT_DIGITAL, DrvJoy1 + 4, "p1 start" }, @@ -453,6 +606,31 @@ static struct BurnInputInfo BlasterInputList[] = { STDINPUTINFO(Blaster) +#define A(a, b, c, d) {a, b, (UINT8*)(c), d} +static struct BurnInputInfo BlasterkitInputList[] = { + {"P1 Coin", BIT_DIGITAL, DrvJoy3 + 4, "p1 coin" }, + {"P2 Coin", BIT_DIGITAL, DrvJoy3 + 5, "p2 coin" }, + {"Coin 3", BIT_DIGITAL, DrvJoy3 + 2, "service2" }, + + {"P1 Start", BIT_DIGITAL, DrvJoy4 + 2, "p1 start" }, + {"P2 Start", BIT_DIGITAL, DrvJoy4 + 3, "p2 start" }, + {"P1 Button 1", BIT_DIGITAL, DrvJoy4 + 0, "p1 fire 1" }, + {"P1 Button 2", BIT_DIGITAL, DrvJoy2 + 0, "p1 fire 2" }, + {"P1 Button 3", BIT_DIGITAL, DrvJoy4 + 1, "p1 fire 3" }, + + A("P1 Stick X", BIT_ANALOG_REL, &DrvAnalogPort0,"p1 x-axis"), + A("P1 Stick Y", BIT_ANALOG_REL, &DrvAnalogPort1,"p1 y-axis"), + + {"Reset", BIT_DIGITAL, &DrvReset, "reset" }, + {"Auto Up / Manual Down", BIT_DIGITAL, DrvJoy3 + 0, "service" }, + {"Advance", BIT_DIGITAL, DrvJoy3 + 1, "diag" }, + {"High Score Reset", BIT_DIGITAL, DrvJoy3 + 3, "service3" }, + {"Tilt", BIT_DIGITAL, DrvJoy3 + 6, "tilt" }, +}; +#undef A + +STDINPUTINFO(Blasterkit) + static struct BurnInputInfo LottofunInputList[] = { {"P1 Coin", BIT_DIGITAL, DrvJoy3 + 4, "p1 coin" }, {"P1 Start", BIT_DIGITAL, DrvJoy1 + 4, "p1 start" }, @@ -645,9 +823,9 @@ static void williams_blitter_write(INT32 offset, UINT8 data) if (w == 0) w = 1; if (h == 0) h = 1; - //bprintf(0, _T("blit: sstart %X dstart %X w %X h %X data %X.\n"), sstart, dstart, w, h, data); accesses = blitter_core(sstart, dstart, w, h, data); + if(data & WMS_BLITTER_CONTROLBYTE_SLOW) { estimated_clocks_at_4MHz = 4 + 4 * (accesses + 2); @@ -656,14 +834,16 @@ static void williams_blitter_write(INT32 offset, UINT8 data) { estimated_clocks_at_4MHz = 4 + 2 * (accesses + 3); } - -// m6809_eat_cycles(-((estimated_clocks_at_4MHz + 3) / 4)); + + if (williams2) { + M6809EatCycles((estimated_clocks_at_4MHz + 3) / 4); // the cpu is halted while the blitter runs + } } static void defender_bank_write(UINT16 address, UINT8 data) { //bprintf (0, _T("BW: %4.4x %2.2x\n"), address, data); - + if (address == 0x03ff) { if (data == 0x39) { BurnWatchdogWrite(); @@ -898,6 +1078,295 @@ static UINT8 williams_main_read(UINT16 address) return 0; } +static INT32 w2_tile_rom_len = 0; +static INT32 bg_rom_len = 0; + +static INT32 DrvGfxDecode() +{ + INT32 thirdBits = (w2_tile_rom_len * 8) / 3; + + w2_numtiles = (thirdBits / 8) / 64; + if (w2_numtiles > 256) w2_numtiles = 256; + + INT32 Plane[4] = { STEP4(0,1) }; + INT32 XOffs[24] = { + 0+0*8+0*thirdBits, 4+0*8+0*thirdBits, 0+0*8+1*thirdBits, 4+0*8+1*thirdBits, 0+0*8+2*thirdBits, 4+0*8+2*thirdBits, + 0+1*8+0*thirdBits, 4+1*8+0*thirdBits, 0+1*8+1*thirdBits, 4+1*8+1*thirdBits, 0+1*8+2*thirdBits, 4+1*8+2*thirdBits, + 0+2*8+0*thirdBits, 4+2*8+0*thirdBits, 0+2*8+1*thirdBits, 4+2*8+1*thirdBits, 0+2*8+2*thirdBits, 4+2*8+2*thirdBits, + 0+3*8+0*thirdBits, 4+3*8+0*thirdBits, 0+3*8+1*thirdBits, 4+3*8+1*thirdBits, 0+3*8+2*thirdBits, 4+3*8+2*thirdBits + }; + INT32 YOffs[16] = { STEP16(0,4*8) }; + + UINT8 *tmp = (UINT8*)BurnMalloc(0x18000); + if (tmp == NULL) { + return 1; + } + memcpy (tmp, DrvTileROM, 0x18000); + + GfxDecode(w2_numtiles, 4, 24, 16, Plane, XOffs, YOffs, 4*16*8, tmp, DrvTileROM); + + BurnFree(tmp); + + return 0; +} + +static UINT32 w2_calc_col(INT32 lo, INT32 hi) +{ + static const float frgb[256] = + { + 0.001889f, 0.002456f, 0.003196f, 0.004384f, 0.005272f, 0.007672f, 0.011345f, 0.017759f, + 0.018643f, 0.03103f, 0.051912f, 0.087251f, 0.1256f, 0.204967f, 0.331644f, 0.529628f, + 0.002524f, 0.003306f, 0.004371f, 0.006072f, 0.007386f, 0.010812f, 0.016096f, 0.02509f, + 0.02687f, 0.043747f, 0.070899f, 0.114042f, 0.159992f, 0.250108f, 0.391104f, 0.607433f, + 0.003522f, 0.004666f, 0.006254f, 0.008766f, 0.010795f, 0.015856f, 0.023585f, 0.036228f, + 0.039487f, 0.062206f, 0.096916f, 0.148811f, 0.20338f, 0.305447f, 0.462741f, 0.700199f, + 0.004941f, 0.006607f, 0.00895f, 0.012619f, 0.015683f, 0.022944f, 0.033832f, 0.050765f, + 0.056018f, 0.085033f, 0.127452f, 0.187886f, 0.25112f, 0.364998f, 0.53879f, 0.797876f, + + 0.006857f, 0.009233f, 0.012592f, 0.017771f, 0.022172f, 0.03212f, 0.046615f, 0.06811f, + 0.075696f, 0.110906f, 0.160678f, 0.229149f, 0.300735f, 0.42593f, 0.61584f, 0.896241f, + 0.010113f, 0.01368f, 0.018726f, 0.02625f, 0.032735f, 0.046462f, 0.065809f, 0.092996f, + 0.103803f, 0.146292f, 0.204648f, 0.282479f, 0.364084f, 0.502781f, 0.712244f, 1.018759f, + 0.015744f, 0.021251f, 0.028944f, 0.039945f, 0.049446f, 0.068127f, 0.093445f, 0.127285f, + 0.14231f, 0.193004f, 0.261153f, 0.349656f, 0.443068f, 0.597642f, 0.830444f, 1.168267f, + 0.024223f, 0.032378f, 0.043467f, 0.058612f, 0.071646f, 0.09563f, 0.127147f, 0.167588f, + 0.187237f, 0.246001f, 0.32398f, 0.423282f, 0.528958f, 0.699986f, 0.957283f, 1.328146f, + + 0.03429f, 0.045182f, 0.059626f, 0.078588f, 0.094882f, 0.123449f, 0.160233f, 0.206173f, + 0.230031f, 0.295596f, 0.382005f, 0.490617f, 0.607092f, 0.792615f, 1.071663f, 1.471949f, + 0.053106f, 0.068185f, 0.087712f, 0.112105f, 0.132967f, 0.167685f, 0.211656f, 0.265082f, + 0.295044f, 0.369837f, 0.467993f, 0.58972f, 0.721623f, 0.927771f, 1.23799f, 1.680648f, + 0.083576f, 0.103924f, 0.129726f, 0.160516f, 0.186838f, 0.228722f, 0.281144f, 0.343382f, + 0.381103f, 0.46708f, 0.5795f, 0.717377f, 0.86859f, 1.100548f, 1.449999f, 1.946101f, + 0.124032f, 0.149904f, 0.182177f, 0.219335f, 0.251408f, 0.300484f, 0.361692f, 0.433113f, + 0.479348f, 0.577076f, 0.704911f, 0.860274f, 1.032601f, 1.292736f, 1.685276f, 2.240223f, + + 0.175706f, 0.207011f, 0.245689f, 0.289706f, 0.327912f, 0.384501f, 0.455038f, 0.536297f, + 0.592068f, 0.702442f, 0.847309f, 1.021943f, 1.217772f, 1.509243f, 1.949852f, 2.570528f, + 0.24927f, 0.287025f, 0.333877f, 0.385984f, 0.431717f, 0.497443f, 0.579727f, 0.673464f, + 0.741572f, 0.868017f, 1.034559f, 1.234123f, 1.460333f, 1.79207f, 2.295118f, 3.001228f, + 0.357357f, 0.403448f, 0.460931f, 0.52375f, 0.579557f, 0.656404f, 0.754715f, 0.865436f, + 0.950203f, 1.097887f, 1.293872f, 1.527752f, 1.795589f, 2.182008f, 2.770169f, 3.593798f, + 0.494255f, 0.549094f, 0.61884f, 0.693823f, 0.760527f, 0.851544f, 0.968276f, 1.098965f, + 1.203912f, 1.377026f, 1.608183f, 1.882244f, 2.200036f, 2.652345f, 3.342626f, 4.215708f + }; + + // default gain/offset per PORT_ADJUSTER defaults: mysticm uses its own monitor_controls_mysticm + // (R=80/G=73/B=81 gain, R=73/G=100/B=78 offset); other games use the generic monitor_controls (25/25/25, 100/100/100) + static const float gain_mysticm[3] = { 0.80f, 0.73f, 0.81f }; + static const float offset_mysticm[3] = { -0.27f, 0.00f, -0.22f }; + static const float gain_generic[3] = { 0.25f, 0.25f, 0.25f }; + static const float offset_generic[3] = { 0.00f, 0.00f, 0.00f }; + const float *gain = is_mysticm ? gain_mysticm : gain_generic; + const float *offset = is_mysticm ? offset_mysticm : offset_generic; + + INT32 i = (hi >> 4) & 15; + INT32 ub = (hi >> 0) & 15; + INT32 ug = (lo >> 4) & 15; + INT32 ur = (lo >> 0) & 15; + + float r = frgb[i * 16 + ur] / 4.22f; + float g = frgb[i * 16 + ug] / 4.22f; + float b = frgb[i * 16 + ub] / 4.22f; + + r = (r + offset[0] > 0.0f) ? r + offset[0] : 0.0f; + g = (g + offset[1] > 0.0f) ? g + offset[1] : 0.0f; + b = (b + offset[2] > 0.0f) ? b + offset[2] : 0.0f; + + r = r * gain[0] / 0.25f; if (r > 1.0f) r = 1.0f; + g = g * gain[1] / 0.25f; if (g > 1.0f) g = 1.0f; + b = b * gain[2] / 0.25f; if (b > 1.0f) b = 1.0f; + + return BurnHighCol((INT32)(r * 255), (INT32)(g * 255), (INT32)(b * 255), 0); +} + +static void w2_update_pen(INT32 entry) +{ + if (DrvPalWritten[entry]) { + DrvPalette[0x10 + entry] = w2_calc_col(DrvPalRAM[entry * 2 + 0], DrvPalRAM[entry * 2 + 1]); + } else { + // never written since power on: placeholder colors + DrvPalette[0x10 + entry] = BurnHighCol((entry & 1) ? 0xff : 0, (entry & 2) ? 0xff : 0, (entry & 4) ? 0xff : 0, 0); + } +} + +static void w2_paletteram_w(INT32 offset) +{ + DrvPalWritten[offset / 2] = 1; + w2_update_pen(offset / 2); +} + +static void w2_rebuild_palette() +{ + for (INT32 i = 0; i < 0x400; i++) + w2_update_pen(i); +} + +// williams2 bank select: +// page 0 : 0x0000-0x7fff videoram +// page 1, 2 : 0x0000-0x7fff rom bank (data & 6) >> 1 +// page 3 : 0x0000-0x7fff rom bank (data & 4) >> 1, 0x8000-0x87ff palette ram + +static void w2_bank_select(UINT8 data) +{ + INT32 page = data & 3; + + switch (page) + { + case 0: + w2_rom_view_active = 0; + w2_palette_view_active = 0; + break; + + case 1: + case 2: + w2_bank_entry = (data & 6) >> 1; + w2_rom_view_active = 1; + w2_palette_view_active = 0; + break; + + case 3: + w2_bank_entry = (data & 4) >> 1; + w2_rom_view_active = 1; + w2_palette_view_active = 1; + break; + } + + if (w2_rom_view_active) { + M6809MapMemory(DrvM6809ROM0 + 0x10000 + w2_bank_entry * 0x8000, 0x0000, 0x7fff, MAP_ROM); + } else { + M6809MapMemory(DrvVidRAM, 0x0000, 0x7fff, MAP_RAM); + } + + if (w2_palette_view_active) { + M6809UnmapMemory(0x8000, 0x87ff, MAP_RAM); // palette ram goes through the handlers + } else { + M6809MapMemory(DrvVidRAM + 0x8000, 0x8000, 0x87ff, MAP_RAM); + } +} + +static void williams2_main_write(UINT16 address, UINT8 data) +{ + if ((address & 0xf800) == 0x8000 && w2_palette_view_active) { + INT32 offset = (address & 0x7ff); + DrvPalRAM[offset] = data; + w2_paletteram_w(offset); + return; + } + + // rom view only overlays reads - writes to 0x0000-0x7fff always land in videoram + if (address < 0x8000) { + DrvVidRAM[address] = data; + return; + } + + if ((address & 0xff80) == 0xc800) { + w2_bank_select_shadow = data; + w2_bank_select(data); + return; + } + + if ((address & 0xff80) == 0xc880) { // mirror(0x0078) + williams_blitter_write(address & 7, data); + return; + } + + if ((address & 0xff80) == 0xc900) { + w2_watchdog_shadow = data; + if ((data & 0x3f) == 0x14) { + BurnWatchdogWrite(); + } + return; + } + + if ((address & 0xff80) == 0xc980) { // mirror(0x0070) + INT32 sel = address & 0xc; + if (sel == 0x0) { pia_write(1, address & 0x3, data); return; } + if (sel == 0x4) { pia_write(0, address & 0x3, data); return; } + if (sel == 0xc) { return; } // this is a 7-segment led + return; + } + + if ((address & 0xffe0) == 0xcb00) { + w2_fg_select_shadow = data; + w2_fg_color = data & 0x3f; + return; + } + + if ((address & 0xffe0) == 0xcb20) { + w2_bg_select_shadow = data; + if (is_mysticm) { + w2_bg_color = data; + } else { + w2_bg_palette_offset = is_joust2 ? ((data & 0x3f) << 4) : ((data & 0x38) << 4); + } + return; + } + + if ((address & 0xffe0) == 0xcb40) { + w2_xscroll_low_shadow = data; + w2_xscroll = (w2_xscroll & ~0x00f) | ((data & 0x80) >> 4) | (data & 0x07); + return; + } + + if ((address & 0xffe0) == 0xcb60) { + w2_xscroll_high_shadow = data; + w2_xscroll = (w2_xscroll & 0x00f) | (data << 4); + return; + } + + if ((address & 0xffe0) == 0xcb80) { + w2_video_control_shadow = data; + return; + } + + if ((address & 0xffe0) == 0xcba0) { + w2_window_enable_shadow = data; + blitter_window_enable = data & 1; + return; + } + + if ((address & 0xfc00) == 0xcc00) { + DrvNVRAM[address & 0x3ff] = data | 0xf0; + return; + } + + if (address >= 0xd000 && address <= 0xdfff) { + DrvM6809RAM0[address & 0xfff] = data; + return; + } +} + +static UINT8 williams2_main_read(UINT16 address) +{ + if ((address & 0xf800) == 0x8000 && w2_palette_view_active) { + return DrvPalRAM[address & 0x7ff]; + } + + // c800-c97f and cb00-cbbf are write-only (unmapped reads) + + if ((address & 0xff80) == 0xc980) { // mirror(0x0070) + INT32 sel = address & 0xc; + if (sel == 0x0) return pia_read(1, address & 3); + if (sel == 0x4) return pia_read(0, address & 3); + return 0; + } + + if ((address & 0xfff0) == 0xcbe0) { + return scanline & 0xff; + } + + if ((address & 0xfc00) == 0xcc00) { + return DrvNVRAM[address & 0x3ff]; + } + + if (address >= 0xd000 && address <= 0xdfff) { + return DrvM6809RAM0[address & 0xfff]; + } + + return 0; +} + static void blaster_bankswitch() { if (vram_select == 0) @@ -957,6 +1426,24 @@ static UINT8 defender_sound_read(UINT16 address) return 0; } +static void williams2_sound_write(UINT16 address, UINT8 data) +{ + address &= ~0x1ffc; + if (address >= 0x2000 && address <= 0x2003) { + pia_write(2, address & 0x3, data); + return; + } +} + +static UINT8 williams2_sound_read(UINT16 address) +{ + address &= ~0x1ffc; + if (address >= 0x2000 && address <= 0x2003) { + return pia_read(2, address & 3); + } + return 0; +} + static void blaster_sound_write(UINT16 address, UINT8 data) { if ((address & 0x7ffc) == 0x0400) { @@ -1014,7 +1501,8 @@ static UINT8 pia0_49way_in_a(UINT16 ) static void sync_sound(INT32 num) { - INT32 cyc = (INT32)(((double)((double)M6809TotalCycles() * 894886) / 1000000)+0.5); + INT32 snd_clock = (williams2) ? 1000000 : 894886; // williams2: MASTER_CLOCK/3/4 = 4MHz/4; williams1: 3.579545MHz/4 + INT32 cyc = (INT32)(((double)((double)M6809TotalCycles() * snd_clock) / 1000000)+0.5); INT32 todo = cyc - M6800TotalCycles(); //bprintf(0, _T("m6809 cyc: %d m6800 cyc: %d. cyc: %d todo %d.\n"), M6809TotalCycles(), M6800TotalCycles(), cyc, todo); // Adding in a couple cycles to prevent lost soundcommands. CPU_RUN_SYNCINT in DrvFrame() will compensate. @@ -1047,11 +1535,13 @@ static void pia1_out_b(UINT16 , UINT8 data) pia_set_input_cb1(2, (l_data == 0xff) ? 0 : 1); M6800Close(); - M6800Open(1); - sync_sound(1); - pia_set_input_b(4, r_data); - pia_set_input_cb1(4, (r_data == 0xff) ? 0 : 1); - M6800Close(); + if (has_second_soundboard) { + M6800Open(1); + sync_sound(1); + pia_set_input_b(4, r_data); + pia_set_input_cb1(4, (r_data == 0xff) ? 0 : 1); + M6800Close(); + } } else { // defender, williams HW (everything else) M6800Open(0); @@ -1068,6 +1558,11 @@ static void pia2_out_b(UINT16 , UINT8 data) DACWrite(0, data); } +static void w2_dac_w(UINT16 , UINT8 data) +{ + DACWrite(0, data); +} + static void pia2_out_b2(UINT16 , UINT8 data) { DACWrite(1, data); @@ -1088,6 +1583,11 @@ static void pia1_main_irq(INT32 state) M6809SetIRQLine(M6809_IRQ_LINE, state ? CPU_IRQSTATUS_ACK : CPU_IRQSTATUS_NONE); } +static void mysticm_pia0_firq(INT32 state) +{ + M6809SetIRQLine(M6809_FIRQ_LINE, state ? CPU_IRQSTATUS_ACK : CPU_IRQSTATUS_NONE); +} + static void pia2_sound_irq(INT32 state) { M6800SetIRQLine(M6800_IRQ_LINE, state ? CPU_IRQSTATUS_ACK : CPU_IRQSTATUS_NONE); @@ -1119,6 +1619,23 @@ static pia6821_interface pia_49way_0 = { NULL, NULL }; +static UINT8 pia0_blasterkit_muxed_in_a(UINT16 ) +{ + return port_select ? pia0_49way_in_a(0) : DrvInputs[3]; // port_select=1: joystick position, =0: IN3 (buttons/start) +} + +static void blasterkit_out_cb2(UINT16 , UINT8 data) +{ + port_select = data; +} + +static pia6821_interface pia_blasterkit_0 = { + pia0_blasterkit_muxed_in_a, pia0_in_b, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, blasterkit_out_cb2, + NULL, NULL +}; + static pia6821_interface pia_1 = { pia1_in_a, NULL, NULL, NULL, NULL, NULL, @@ -1147,6 +1664,169 @@ static pia6821_interface pia_2_sinistar = { pia2_sound_irq, pia2_sound_irq }; +// Williams 2 +static UINT8 w2_mux_in_a(UINT16 ) +{ + return (port_select == 0) ? DrvInputs[3] : DrvInputs[4]; +} + +static UINT8 tshoot_gun_r(INT32 axis) +{ + INT16 raw = (axis ? BurnGunReturnX(0) : BurnGunReturnY(0)); + INT32 data = (raw >> 2) & 0x3f; // scale 0-255 down to 0-0x3f + UINT8 result = (data & 0x3f) ^ ((data & 0x3f) >> 1); // gray-code + return result; +} + +static UINT8 tshoot_mux_in_a(UINT16 ) +{ + UINT8 ret = tshoot_gun_r(port_select & 1) | (DrvInputs[3] & 0xc0); + return ret; +} + +static UINT8 joust2_mux_in_a(UINT16 ) +{ + return ((DrvInputs[0] & 0xf0) ^ 0xf0) | (w2_mux_in_a(0) & 0x0f); +} + +static void w2_out_ca2(UINT16 , UINT8 data) +{ + port_select = data; +} + +static void w2_snd_cmd_w(UINT16 , UINT8 data) +{ + M6800Open(0); + sync_sound(0); + pia_set_input_a(2, data); + M6800Close(); +} + +static void w2_pia1_cb2_to_pia2_ca1(UINT16 , UINT8 data) +{ + M6800Open(0); + sync_sound(0); + pia_set_input_ca1(2, data); + M6800Close(); +} + +static void w2_pia2_ca2_to_pia1_cb1(UINT16 , UINT8 data) +{ + pia_set_input_cb1(1, data); +} + +#define BG_CB2_QUEUE 16 +static UINT8 bg_cb2_queue[BG_CB2_QUEUE]; +static INT32 bg_cb2_count = 0; +static INT32 bg_resetq_state = 1; + +static void joust2_bg_cb2_w(INT32 state) +{ + if (bg_cb2_count < BG_CB2_QUEUE) bg_cb2_queue[bg_cb2_count++] = state; +} + +static void joust2_bg_cb2_flush() +{ + for (INT32 i = 0; i < bg_cb2_count; i++) { + pia_set_input_cb1(2, bg_cb2_queue[i]); + } + bg_cb2_count = 0; +} + +static void joust2_bg_strobe_w(UINT16 , UINT8 state) +{ + cvsd_cb1_write(state); +} + +static void joust2_bg_resetq_w(UINT16 , UINT8 state) +{ + if (bg_resetq_state && !state) { + cvsd_cpu_reset(); + } + bg_resetq_state = state; +} + +static void pia2a_to_pia1_out_b(UINT16 , UINT8 data) +{ + pia_set_input_b(1, data); +} + +static pia6821_interface pia_w2_0 = { + w2_mux_in_a, pia0_in_a, + NULL, NULL, NULL, NULL, + NULL, NULL, w2_out_ca2, NULL, + NULL, NULL +}; + +static pia6821_interface pia_tshoot_0 = { + tshoot_mux_in_a, pia0_in_b, + NULL, NULL, NULL, NULL, + NULL, NULL, w2_out_ca2, NULL, + pia1_main_irq, pia1_main_irq +}; + +static pia6821_interface pia_mysticm_0 = { + pia0_in_a, pia0_in_b, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + mysticm_pia0_firq, pia1_main_irq +}; + +static pia6821_interface pia_joust2_0 = { + joust2_mux_in_a, pia0_in_b, + NULL, NULL, NULL, NULL, + NULL, NULL, w2_out_ca2, NULL, + NULL, NULL +}; + +static pia6821_interface pia_w2_1 = { + pia0_in_b, NULL, + NULL, NULL, NULL, NULL, + NULL, w2_snd_cmd_w, NULL, w2_pia1_cb2_to_pia2_ca1, + pia1_main_irq, pia1_main_irq +}; + +static pia6821_interface pia_tshoot_1 = { + pia1_in_a, NULL, + NULL, NULL, NULL, NULL, + NULL, w2_snd_cmd_w, NULL, w2_pia1_cb2_to_pia2_ca1, + pia1_main_irq, pia1_main_irq +}; + +static pia6821_interface pia_mysticm_1 = { + pia1_in_a, NULL, + NULL, NULL, NULL, NULL, + NULL, w2_snd_cmd_w, NULL, w2_pia1_cb2_to_pia2_ca1, + pia1_main_irq, pia1_main_irq +}; + +static void joust2_snd_cmd_w(UINT16 address, UINT8 data) +{ + w2_snd_cmd_w(address, data); + cvsd_portb_write(data); // goes to the sound cpu and to the bg board +} + +static pia6821_interface pia_joust2_1 = { + pia1_in_a, NULL, + NULL, NULL, NULL, NULL, + NULL, joust2_snd_cmd_w, joust2_bg_strobe_w, w2_pia1_cb2_to_pia2_ca1, + pia1_main_irq, pia1_main_irq +}; + +static pia6821_interface pia_w2_2 = { + NULL, NULL, + NULL, NULL, NULL, NULL, + pia2a_to_pia1_out_b, w2_dac_w, w2_pia2_ca2_to_pia1_cb1, NULL, + pia2_sound_irq, pia2_sound_irq +}; + +static pia6821_interface pia_joust2_2 = { + NULL, NULL, + NULL, NULL, NULL, NULL, + pia2a_to_pia1_out_b, w2_dac_w, w2_pia2_ca2_to_pia1_cb1, joust2_bg_resetq_w, + pia2_sound_irq, pia2_sound_irq +}; + static UINT8 pia3_in_a(UINT16 ) { return DrvInputs[3]; @@ -1168,17 +1848,21 @@ static INT32 DrvDoReset(INT32 clear_mem) { if (clear_mem) { memset (AllRam, 0, RamEnd - AllRam); + if (williams2) w2_rebuild_palette(); } M6809Open(0); M6809Reset(); + if (williams2) { + w2_bank_select(0); + } M6809Close(); M6800Open(0); M6800Reset(); M6800Close(); - if (blaster || sinistarc) { + if ((blaster || sinistarc) && has_second_soundboard) { M6800Open(1); M6800Reset(); M6800Close(); @@ -1193,6 +1877,12 @@ static INT32 DrvDoReset(INT32 clear_mem) if (uses_hc55516) hc55516_reset(); + if (has_s11_bg) { + cvsd_reset(); + bg_cb2_count = 0; + bg_resetq_state = 1; + } + cocktail = 0; bankselect = 0; vram_select = 0; @@ -1206,6 +1896,11 @@ static INT32 DrvDoReset(INT32 clear_mem) TrackX[0] = TrackX[1] = 0; TrackY[0] = TrackY[1] = 0; + conquest_dial = 0; + + w2_fg_color = 0; + w2_bg_palette_offset = 0; + w2_xscroll = 0; nExtraCycles[0] = nExtraCycles[1] = nExtraCycles[2] = 0; @@ -1221,26 +1916,33 @@ static INT32 MemIndex() DrvM6809ROM0 = Next; Next += 0x050000; DrvM6800ROM0 = Next; Next += 0x010000; DrvM6800ROM1 = Next; Next += 0x010000; - + DrvGfxROM = Next; Next += 0x018000; - + DrvColPROM = Next; Next += 0x001000; + DrvTileROM = Next; Next += 0x018000; // up to 256 tiles * 384 bytes (joust2 has double-size tile ROMs -> 256 tiles) + + DrvBgROM = Next; Next += 0x080000; // bg board, laid out as U4 / U19 / U20 at 0x00000 / 0x20000 / 0x40000 + Palette = (UINT32*)Next; Next += 0x0100 * sizeof(UINT32); - DrvPalette = (UINT32*)Next; Next += (0x0010 + 0x0100) * sizeof(UINT32); + DrvPalette = (UINT32*)Next; Next += (0x0010 + 0x0400) * sizeof(UINT32); DrvNVRAM = Next; Next += 0x000400; blitter_remap = Next; Next += 0x010000; - + AllRam = Next; DrvM6809RAM0 = Next; Next += 0x004000; DrvM6800RAM0 = Next; Next += 0x000100; DrvM6800RAM1 = Next; Next += 0x000100; DrvVidRAM = Next; Next += 0x00c000; - DrvPalRAM = Next; Next += 0x000010; + DrvPalRAM = Next; Next += 0x000800; DrvBlitRAM = Next; Next += 0x000008; + DrvTileRAM = Next; Next += 0x000800; + DrvPalWritten = Next; Next += 0x000400; + DrvBgRAM = Next; Next += 0x000800; RamEnd = Next; @@ -1257,6 +1959,9 @@ static INT32 DrvRomLoad(INT32 type) // 1-defender, 2-mysticm, 3-tshoot, 4-sinist UINT8 *cLoad = DrvColPROM; UINT8 *gLoad = DrvGfxROM; + has_second_soundboard = 0; + bg_rom_len = 0; + if (type == 4) mLoad += 0x1000; for (INT32 i = 0; !BurnDrvGetRomName(&pRomName, i, 0); i++) @@ -1268,15 +1973,24 @@ static INT32 DrvRomLoad(INT32 type) // 1-defender, 2-mysticm, 3-tshoot, 4-sinist // blaster if (ri.nLen == 0x4000 && offset == 0x15000) mLoad += 0x3000; // gap + // conquest (incomplete prototype dump - 0xd000-0xdfff is unpopulated on PCB) + if (type == 6 && offset == 0xd000) mLoad += 0x1000; // gap + // williams2/inferno: fixed window starts at 0xe000 (not 0xd000), and bank 1 (0x18000-0x1ffff) is unpopulated + if (type == 7 && offset == 0xd000) mLoad += 0x1000; // gap + if (type == 7 && offset == 0x18000) mLoad += 0x8000; // gap (empty bank) + // mysticm: fixed window starts at 0xe000 (like inferno); bank1 has only 3 of 4 chips + if (type == 9 && offset == 0xd000) mLoad += 0x1000; // gap + if (type == 9 && offset == 0x1e000) mLoad += 0x2000; // gap + // joust2: 0xd000-0xdfff is real ROM (unlike inferno); bank2 (0x20000-0x27fff) only has 3 of 4 chips + if (type == 8 && offset == 0x26000) mLoad += 0x2000; // gap if (type == 2) { if (offset == 0x18000) mLoad += 0x8000; if (offset == 0x24000) mLoad += 0xc000; if (offset == 0x38000) mLoad += 0x8000; } if (type == 3) { - if (offset == 0x18000) mLoad += 0x8000; - if (offset == 0x26000) mLoad += 0xa000; - if (offset == 0x38000) mLoad += 0x8000; + // tshoot: bank1 (0x18000-0x1ffff) only has 2 of 4 chips - second half is empty + if (offset == 0x1c000) mLoad += 0x4000; } //bprintf(0, _T("loading main ROM @ %04X (%X bytes)\n"), offset, ri.nLen); if (BurnLoadRom(mLoad, i, 1)) return 1; @@ -1295,6 +2009,7 @@ static INT32 DrvRomLoad(INT32 type) // 1-defender, 2-mysticm, 3-tshoot, 4-sinist if ((ri.nType & 7) == 3) { bprintf(0, _T("loading second sound rom %x\n"), ri.nLen); + has_second_soundboard = 1; memmove (DrvM6800ROM1, DrvM6800ROM1 + ri.nLen, 0x10000 - ri.nLen); // move back if (BurnLoadRom(DrvM6800ROM1 + 0x10000 - ri.nLen, i, 1)) return 1; continue; @@ -1312,11 +2027,29 @@ static INT32 DrvRomLoad(INT32 type) // 1-defender, 2-mysticm, 3-tshoot, 4-sinist gLoad += ri.nLen; continue; } + + if ((ri.nType & 7) == 6) { + if (BurnLoadRom(DrvTileROM + w2_tile_rom_len, i, 1)) return 1; + w2_tile_rom_len += ri.nLen; + continue; + } + + if ((ri.nType & 7) == 7) { // bg board: each 27256 is mirrored over its 0x20000 slot + UINT8 *dst = DrvBgROM + bg_rom_len * 0x20000; + if (BurnLoadRom(dst, i, 1)) return 1; + for (INT32 j = 1; j < 4; j++) memcpy(dst + j * 0x8000, dst, 0x8000); + bg_rom_len++; + continue; + } } // colony if ((mLoad - DrvM6809ROM0) == 0x12800) memcpy (DrvM6809ROM0 + 0x12800, DrvM6809ROM0 + 0x12000, 0x0800); + if (type == 7) memset(DrvM6809ROM0 + 0x18000, 0xff, 0x8000); // inferno: bank 1 (0x18000-0x1ffff) unpop. + if (type == 3) memset(DrvM6809ROM0 + 0x1c000, 0xff, 0x4000); // tshoot: bank1 second half unpopulated + if (type == 9) memset(DrvM6809ROM0 + 0x1e000, 0xff, 0x2000); // mysticm: bank1 last chip unpopulated + return 0; } @@ -1347,14 +2080,16 @@ static INT32 DrvInit(INT32 maptype, INT32 loadtype, INT32 x_adjust, INT32 blitte if (DrvRomLoad(loadtype)) return 1; } - // sound HW - M6800Init(0); - M6800Open(0); - M6800MapMemory(DrvM6800RAM0, 0x0000, 0x00ff, MAP_RAM); - M6800MapMemory(DrvM6800ROM0 + 0xb000, 0xb000, 0xffff, MAP_ROM); - M6800SetWriteHandler(defender_sound_write); - M6800SetReadHandler(defender_sound_read); - M6800Close(); + if (maptype != 3) { + // pre-williams2 sound HW + M6800Init(0); + M6800Open(0); + M6800MapMemory(DrvM6800RAM0, 0x0000, 0x00ff, MAP_RAM); + M6800MapMemory(DrvM6800ROM0 + 0xb000, 0xb000, 0xffff, MAP_ROM); + M6800SetWriteHandler(defender_sound_write); + M6800SetReadHandler(defender_sound_read); + M6800Close(); + } if (maptype == 2) // blaster { @@ -1367,13 +2102,15 @@ static INT32 DrvInit(INT32 maptype, INT32 loadtype, INT32 x_adjust, INT32 blitte M6809SetReadHandler(williams_main_read); M6809Close(); - M6800Init(1); - M6800Open(1); - M6800MapMemory(DrvM6800RAM1, 0x0000, 0x00ff, MAP_RAM); - M6800MapMemory(DrvM6800ROM1 + 0xb000, 0xb000, 0xffff, MAP_ROM); - M6800SetWriteHandler(blaster_sound_write); - M6800SetReadHandler(blaster_sound_read); - M6800Close(); + if (has_second_soundboard) { + M6800Init(1); + M6800Open(1); + M6800MapMemory(DrvM6800RAM1, 0x0000, 0x00ff, MAP_RAM); + M6800MapMemory(DrvM6800ROM1 + 0xb000, 0xb000, 0xffff, MAP_ROM); + M6800SetWriteHandler(blaster_sound_write); + M6800SetReadHandler(blaster_sound_read); + M6800Close(); + } } else if (maptype == 1) // williams { @@ -1401,32 +2138,87 @@ static INT32 DrvInit(INT32 maptype, INT32 loadtype, INT32 x_adjust, INT32 blitte M6809SetReadHandler(defender_main_read); M6809Close(); } + else if (maptype == 3) // williams2 (inferno, joust2, tshoot, mysticm) + { + williams2 = 1; + bprintf(0, _T("Williams 2 DrvInit()!\n")); + DrvGfxDecode(); + + M6800Init(0); + M6800Open(0); + M6800MapMemory(DrvM6800RAM0, 0x0000, 0x00ff, MAP_RAM); + M6800MapMemory(DrvM6800ROM0 + 0xe000, 0xe000, 0xffff, MAP_ROM); + M6800SetWriteHandler(williams2_sound_write); + M6800SetReadHandler(williams2_sound_read); + M6800Close(); + + M6809Init(0); + M6809Open(0); + M6809MapMemory(DrvVidRAM + 0x8800 ,0x8800, 0xbfff, MAP_RAM); + M6809MapMemory(DrvTileRAM ,0xc000, 0xc7ff, MAP_RAM); + M6809MapMemory(DrvNVRAM ,0xcc00, 0xcfff, MAP_ROM); + if (is_joust2 || is_tshoot) { + M6809MapMemory(DrvM6809ROM0 + 0xd000, 0xd000, 0xdfff, MAP_ROM); + } else { + M6809MapMemory(DrvM6809RAM0 ,0xd000, 0xdfff, MAP_RAM); + } + M6809MapMemory(DrvM6809ROM0 + 0xe000,0xe000, 0xffff, MAP_ROM); + M6809SetWriteHandler(williams2_main_write); + M6809SetReadHandler(williams2_main_read); + M6809Close(); + + } pia_init(); - pia_config(0, 0, &pia_0); - pia_config(1, 0, &pia_1); - pia_config(2, 0, &pia_2); - pia_config(3, 0, &pia_3); + if (williams2) { + pia_config(0, 0, is_joust2 ? &pia_joust2_0 : is_tshoot ? &pia_tshoot_0 : is_mysticm ? &pia_mysticm_0 : &pia_w2_0); + pia_config(1, 0, is_joust2 ? &pia_joust2_1 : is_tshoot ? &pia_tshoot_1 : is_mysticm ? &pia_mysticm_1 : &pia_w2_1); + pia_config(2, 0, is_joust2 ? &pia_joust2_2 : &pia_w2_2); + if (!is_joust2 && !is_tshoot && !is_mysticm) { + pia_set_port_a_input_overrides_output_mask(2, 0xff); // inferno: sound commands can drive pia2 port a output pins + } + } else { + pia_config(0, 0, &pia_0); + pia_config(1, 0, &pia_1); + pia_config(2, 0, &pia_2); + pia_config(3, 0, &pia_3); + } BurnWatchdogInit(DrvDoReset, 180); - DACInit(0, 0, 0, M6800TotalCycles, 894886); - DACSetRoute(0, 0.35, BURN_SND_ROUTE_BOTH); + DACInit(0, 0, 0, M6800TotalCycles, (williams2) ? 1000000 : 894886); + DACSetRoute(0, (williams2) ? 0.13 : 0.35, BURN_SND_ROUTE_BOTH); DACDCBlock(1); + if (has_s11_bg) { // just joust2 + cvsd_init(1, 1, 3, DrvBgROM, DrvBgRAM); + cvsd_set_cb2_callback(joust2_bg_cb2_w); + cvsd_set_s11_bg_mode(1); + cvsd_set_dac_signed(1); + + BurnYM2151SetRoute(BURN_SND_YM2151_YM2151_ROUTE_1, 0.10, BURN_SND_ROUTE_BOTH); + BurnYM2151SetRoute(BURN_SND_YM2151_YM2151_ROUTE_2, 0.10, BURN_SND_ROUTE_BOTH); + DACSetRoute(1, 0.10, BURN_SND_ROUTE_BOTH); + hc55516_volume(0.43); // in cvsd! + } + if (maptype == 2) // blaster, pia config & l+r dac { pia_init(); - pia_config(0, 0, &pia_49way_0); + pia_config(0, 0, is_blasterkit ? &pia_blasterkit_0 : &pia_49way_0); pia_config(1, 0, &pia_1); pia_config(2, 0, &pia_2); pia_config(3, 0, &pia_3); pia_config(4, 0, &pia_4); // 2nd soundboard - DACSetRoute(0, 0.35, BURN_SND_ROUTE_LEFT); + if (has_second_soundboard) { + DACSetRoute(0, 0.35, BURN_SND_ROUTE_LEFT); - DACInit(1, 0, 0, M6800TotalCycles, 894886); - DACSetRoute(1, 0.35, BURN_SND_ROUTE_RIGHT); + DACInit(1, 0, 0, M6800TotalCycles, 894886); + DACSetRoute(1, 0.35, BURN_SND_ROUTE_RIGHT); + } else { + DACSetRoute(0, 0.35, BURN_SND_ROUTE_BOTH); // mono kit - single board feeds both channels + } } blitter_clip_address = blitter_clip_addr; @@ -1436,6 +2228,8 @@ static INT32 DrvInit(INT32 maptype, INT32 loadtype, INT32 x_adjust, INT32 blitte screen_x_adjust = x_adjust; + if (is_tshoot) BurnGunInit(1, true); + DrvDoReset(1); return 0; @@ -1443,6 +2237,7 @@ static INT32 DrvInit(INT32 maptype, INT32 loadtype, INT32 x_adjust, INT32 blitte static INT32 DrvExit() { + if (is_tshoot) BurnGunExit(); GenericTilesExit(); M6809Exit(); @@ -1454,6 +2249,9 @@ static INT32 DrvExit() if (uses_hc55516) hc55516_exit(); + if (has_s11_bg) + cvsd_exit(); + BurnFreeMemIndex(); memset (DrvDips, 0, 3); @@ -1467,9 +2265,22 @@ static INT32 DrvExit() spdball = 0; playball = 0; lottofun = 0; + conquest = 0; + + williams2 = 0; + is_joust2 = 0; + is_tshoot = 0; + is_mysticm = 0; + has_s11_bg = 0; + + w2_tile_rom_len = 0; + w2_rom_view_active = 0; + w2_palette_view_active = 0; + w2_bank_entry = 0; uses_hc55516 = 0; uses_colprom = 0; + has_s11_bg = 0; pStartDraw = NULL; pDrawScanline = NULL; @@ -1529,6 +2340,88 @@ static void draw_bitmap(INT32 starty, INT32 endy) } } +static INT32 mysticm_color_decode(INT32 base_col, INT32 sig_J1, INT32 y) +{ + INT32 v = y << 6; + INT32 sig_W11 = (v >> 11) & 1; + INT32 sig_W12 = (v >> 12) & 1; + INT32 sig_W13 = (v >> 13) & 1; + + INT32 a = 1 | ((base_col & 1) << 2) | ((base_col & 1) << 3); + INT32 b = (sig_W12 << 0) | (sig_W13 << 1) | (0 << 2) | (sig_J1 << 3); + INT32 color = (a > b) || ((a == b) && !sig_W11); + + return (base_col & 0x3e) | color; +} + +static void w2_draw_bitmap(INT32 starty, INT32 endy) +{ + // visible area is x 8-283, y 8-247 of the hardware screen + for (INT32 y = starty; y < endy; y++) + { + if (y >= nScreenHeight) return; + + UINT16 *dst = pTransDraw + (y * nScreenWidth); + INT32 sy = y + 8; + + INT32 tile_row = (sy >> 4) & 15; // 16 rows of 16px-tall tiles + INT32 fine_y = sy & 15; + + // background: tilemap (128 cols x 16 rows, 24x16 tiles, TILEMAP_SCAN_COLS) + INT32 w2_scrollx_px = (w2_xscroll & 7) + ((w2_xscroll >> 3) * 6); + for (INT32 x = 0; x < nScreenWidth; x++) + { + INT32 scrolled_x = (x + 8) + w2_scrollx_px - 2; + INT32 tile_col = (scrolled_x / 24) & 127; + INT32 fine_x = scrolled_x % 24; + + INT32 tile_index = tile_col * 16 + tile_row; + UINT8 tile_data = DrvTileRAM[tile_index & 0x7ff]; + INT32 tile_num, flipx; + if (is_joust2) { + tile_num = tile_data; + flipx = 0; + } else { + tile_num = tile_data & 0x7f; + flipx = tile_data & 0x80; + } + INT32 color, bg_off; + + if (is_mysticm) { + color = mysticm_color_decode(w2_bg_color, 0, (tile_index << 4) & 0xff) & 0x3f; + bg_off = 0; + } else if (is_joust2) { + color = 0; + bg_off = w2_bg_palette_offset; + } else { + color = (tile_index >> 1) & 7; + bg_off = w2_bg_palette_offset; + } + + INT32 px = flipx ? (23 - fine_x) : fine_x; + + UINT8 pix = 0; + if (tile_num < w2_numtiles) + pix = DrvTileROM[tile_num * (24 * 16) + fine_y * 24 + px]; + + INT32 idx = 0x10 + bg_off + color * 16 + pix; + dst[x] = idx; + + } + + // foreground: mix in the blitter + INT32 fg_col = is_mysticm ? (mysticm_color_decode(w2_fg_color, 1, sy) & 0x3f) : w2_fg_color; + UINT8 *src = DrvVidRAM + sy; + for (INT32 x = 0; x < nScreenWidth; x += 2) + { + UINT8 pix = src[((x + 8) / 2) * 256]; + + if (pix & 0xf0) dst[x + 0] = 0x10 + fg_col * 16 + (pix >> 4); + if (pix & 0x0f) dst[x + 1] = 0x10 + fg_col * 16 + (pix & 0x0f); + } + } +} + static void blaster_draw_bitmap(INT32 starty, INT32 endy) { const UINT8 YOFFS = 7; @@ -1602,6 +2495,32 @@ static void DrvDrawEnd() BurnTransferCopy(DrvPalette); } +static INT32 Williams2DrawEnd() +{ + if (DrvRecalc) { + w2_rebuild_palette(); + DrvRecalc = 0; + } + + BurnTransferCopy(DrvPalette); + + return 0; +} + +static INT32 Williams2Draw() +{ + if (DrvRecalc) { + w2_rebuild_palette(); + DrvRecalc = 0; + } + + w2_draw_bitmap(0, nScreenHeight); + + BurnTransferCopy(DrvPalette); + + return 0; +} + static INT32 DrvDraw() { if (DrvRecalc) { @@ -1637,7 +2556,7 @@ static INT32 BlasterDraw() static INT32 DrvFrame() { BurnWatchdogUpdate(); - + if (DrvReset) { DrvDoReset(1); } @@ -1652,8 +2571,15 @@ static INT32 DrvFrame() memset (DrvInputs + 3, 0, 7-3); + if (williams2) { + DrvInputs[3] = 0xff; // active low + DrvInputs[4] = 0xff; + } + + if (is_tshoot) BurnGunMakeInputs(0, DrvAnalogPort0, DrvAnalogPort1); + if (lottofun) { - DrvInputs[2] |= 1; // Memory Protect ON by default (for clean boot) + DrvInputs[2] |= 1; // Memory Protect MemoryProtect.Toggle(DrvJoy3[0]); } @@ -1707,11 +2633,21 @@ static INT32 DrvFrame() if (xy < (0x80-0x10)) TrackX[1]-=12; } + + if (conquest) { + UINT8 xy = ProcessAnalog(DrvAnalogPort0, 0, 1, 0x00, 0xff); + if (xy > (0x80+0x10)) conquest_dial--; + if (xy < (0x80-0x10)) conquest_dial++; + conquest_dial &= 0x0f; + + DrvInputs[0] = (DrvInputs[0] & ~0x0c) | (((conquest_dial >> 2) & 3) << 2); + DrvInputs[1] = (DrvInputs[1] & ~0x03) | (conquest_dial & 3); + } } - INT32 nInterleave = 256; - INT32 nCyclesTotal[3] = { 1000000 / 60, 894886 / 60, 894886 / 60 }; - INT32 nCyclesDone[3] = { nExtraCycles[0], nExtraCycles[1], nExtraCycles[2] }; + INT32 nInterleave = (williams2 && !is_mysticm) ? 260 : 256; // williams2: 260 lines per frame (mysticm: 256) + INT32 nCyclesTotal[4] = { 1000000 / 60, (williams2 ? 1000000 : 894886) / 60, 894886 / 60, 2000000 / 60 /*s11bg*/ }; + INT32 nCyclesDone[4] = { nExtraCycles[0], nExtraCycles[1], nExtraCycles[2], 0 }; M6809Open(0); @@ -1723,47 +2659,72 @@ static INT32 DrvFrame() CPU_RUN(0, M6809); - if (scanline % 8 == 0) { - pia_set_input_cb1(1, scanline & 0x20); - if (pDrawScanline) pDrawScanline(); + if (williams2) { + if ((scanline & 0x1f) == 0 && scanline < 256) { // va11 + pia_set_input_cb1(0, scanline & 0x20); + pia_set_input_ca1(1, scanline & 0x20); + } + if (scanline == 8 || scanline == 254) { + pia_set_input_ca1(0, (scanline >= 254) ? 0 : 1); + } + + if (pBurnDraw && scanline >= 8 && scanline < 8 + nScreenHeight) { + w2_draw_bitmap(scanline - 8, scanline - 7); + } + } else { + if (scanline % 8 == 0) { + pia_set_input_cb1(1, scanline & 0x20); + if (pDrawScanline) pDrawScanline(); + } + + if (scanline == 0 || scanline == 240) + pia_set_input_ca1(1, scanline >= 240 ? 1 : 0); } - if (scanline == 0 || scanline == 240) - pia_set_input_ca1(1, scanline >= 240 ? 1 : 0); - M6800Open(0); + if (has_s11_bg) joust2_bg_cb2_flush(); // slightly delayed message(s) from s11bg -> main sound CPU_RUN_SYNCINT(1, M6800); M6800Close(); - if (blaster || sinistarc) { + if (has_s11_bg) { + M6809CPUPush(1); + CPU_RUN_TIMER(3); + M6809CPUPop(); + } + + if ((blaster || sinistarc) && has_second_soundboard) { M6800Open(1); CPU_RUN_SYNCINT(2, M6800); M6800Close(); } } - if (pBurnSoundOut) { - M6800Open(0); - DACUpdate(pBurnSoundOut, nBurnSoundLen); - if (uses_hc55516) - hc55516_update(pBurnSoundOut, nBurnSoundLen); - M6800Close(); - } - M6809Close(); + if (pBurnSoundOut) { + if (has_s11_bg) { + cvsd_update(pBurnSoundOut, nBurnSoundLen); // ym2151, dac(main, cvsd board), hc55516 + } else { + DACUpdate(pBurnSoundOut, nBurnSoundLen); + if (uses_hc55516) + hc55516_update(pBurnSoundOut, nBurnSoundLen); + } + } + nExtraCycles[0] = nCyclesDone[0] - nCyclesTotal[0]; M6800Open(0); nExtraCycles[1] = M6800TotalCycles() - nCyclesTotal[1]; M6800Close(); - if (blaster || sinistarc) { + if ((blaster || sinistarc) && has_second_soundboard) { M6800Open(1); nExtraCycles[2] = M6800TotalCycles() - nCyclesTotal[2]; M6800Close(); } if (pBurnDraw) { - if (pStartDraw) + if (williams2) + Williams2DrawEnd(); + else if (pStartDraw) DrvDrawEnd(); else BurnDrvRedraw(); @@ -1774,19 +2735,12 @@ static INT32 DrvFrame() static INT32 DrvScan(INT32 nAction, INT32 *pnMin) { - struct BurnArea ba; - if (pnMin) { *pnMin = 0x029702; } if (nAction & ACB_VOLATILE) { - memset(&ba, 0, sizeof(ba)); - - ba.Data = AllRam; - ba.nLen = RamEnd - AllRam; - ba.szName = "All Ram"; - BurnAcb(&ba); + ScanVar(AllRam, RamEnd - AllRam, "All Ram"); M6809Scan(nAction); M6800Scan(nAction); @@ -1815,19 +2769,43 @@ static INT32 DrvScan(INT32 nAction, INT32 *pnMin) SCAN_VAR(TrackX); SCAN_VAR(TrackY); } + + if (has_s11_bg) { + cvsd_scan(nAction, pnMin); + SCAN_VAR(bg_resetq_state); + } + + if (is_tshoot) BurnGunScan(); + + if (williams2) { + SCAN_VAR(port_select); + SCAN_VAR(blitter_window_enable); + SCAN_VAR(w2_fg_color); + SCAN_VAR(w2_bg_color); + SCAN_VAR(w2_bg_palette_offset); + SCAN_VAR(w2_xscroll); + SCAN_VAR(w2_bank_select_shadow); + SCAN_VAR(w2_fg_select_shadow); + SCAN_VAR(w2_bg_select_shadow); + SCAN_VAR(w2_xscroll_low_shadow); + SCAN_VAR(w2_xscroll_high_shadow); + SCAN_VAR(w2_video_control_shadow); + SCAN_VAR(w2_window_enable_shadow); + SCAN_VAR(w2_watchdog_shadow); + } } if (nAction & ACB_NVRAM) { - ba.Data = DrvNVRAM; - ba.nLen = 0x00400; - ba.nAddress = 0; - ba.szName = "NVRAM"; - BurnAcb(&ba); + ScanVar(DrvNVRAM, 0x400, "NVRAM"); } if (nAction & ACB_WRITE) { M6809Open(0); - if (blaster) { + if (williams2) { + w2_bank_select(w2_bank_select_shadow); + DrvRecalc = 1; + } + else if (blaster) { blaster_bankswitch(); } else if (defender) { @@ -2713,6 +3691,354 @@ struct BurnDriver BurnDrvStargate = { }; +// Conquest (prototype) + +static struct BurnRomInfo conquestRomDesc[] = { + { "conquest_b.bin", 0x1000, 0x9aab5516, 1 | BRF_PRG | BRF_ESS }, // 0 M6809 Code (fixed window, mirrored) + { "conquest_b.bin", 0x1000, 0x9aab5516, 1 | BRF_PRG | BRF_ESS }, // 1 (mirror of above) + { "conquest_a.bin", 0x4000, 0xa384f4a2, 1 | BRF_PRG | BRF_ESS }, // 2 (banked) + + { "video_sound_rom_1.ic12", 0x0800, 0xfefd5b48, 2 | BRF_PRG | BRF_ESS }, // 3 M6800 Code + + { "decoder.2", 0x0200, 0x8dd98da5, 0 | BRF_OPT }, // 4 Address Decoder + { "decoder.3", 0x0200, 0xc3f45f70, 0 | BRF_OPT }, // 5 +}; + +STD_ROM_PICK(conquest) +STD_ROM_FN(conquest) + +static INT32 ConquestInit() +{ + conquest = 1; + + return DrvInit(1, 6, 6, -1, 0); +} + +struct BurnDriverD BurnDrvConquest = { + "conquest", NULL, NULL, NULL, "1982", + "Conquest (prototype)\0", NULL, "Williams / Vid Kidz", "6809 System", + NULL, NULL, NULL, NULL, + BDF_GAME_NOT_WORKING | BDF_ORIENTATION_VERTICAL, 2, HARDWARE_MISC_PRE90S, GBF_HORSHOOT, 0, + NULL, conquestRomInfo, conquestRomName, NULL, NULL, NULL, NULL, ConquestInputInfo, NULL, + ConquestInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0x10, + 240, 292, 3, 4 +}; + + +// Inferno + +static struct BurnRomInfo infernoRomDesc[] = { + { "ic9.inf", 0x1000, 0x1a013185, 1 | BRF_PRG | BRF_ESS }, // 0 M6809 Code (fixed window) + { "ic10.inf", 0x1000, 0xdbf64a36, 1 | BRF_PRG | BRF_ESS }, // 1 + + { "ic18.inf", 0x2000, 0x95bcf7b1, 1 | BRF_PRG | BRF_ESS }, // 2 (bank 0) + { "ic16.inf", 0x2000, 0x8bc4f935, 1 | BRF_PRG | BRF_ESS }, // 3 + { "ic14.inf", 0x2000, 0xa70508a7, 1 | BRF_PRG | BRF_ESS }, // 4 + { "ic12.inf", 0x2000, 0x7ffb87f9, 1 | BRF_PRG | BRF_ESS }, // 5 + + { "ic17.inf", 0x2000, 0xb4684139, 1 | BRF_PRG | BRF_ESS }, // 6 (bank 2) + { "ic15.inf", 0x2000, 0x128a6ad6, 1 | BRF_PRG | BRF_ESS }, // 7 + { "ic13.inf", 0x2000, 0x83a9e4d6, 1 | BRF_PRG | BRF_ESS }, // 8 + { "ic11.inf", 0x2000, 0xc2e9c909, 1 | BRF_PRG | BRF_ESS }, // 9 + + { "ic25.inf", 0x2000, 0x103a5951, 1 | BRF_PRG | BRF_ESS }, // 10 (bank 3) + { "ic23.inf", 0x2000, 0xc04749a0, 1 | BRF_PRG | BRF_ESS }, // 11 + { "ic21.inf", 0x2000, 0xc405f853, 1 | BRF_PRG | BRF_ESS }, // 12 + { "ic19.inf", 0x2000, 0xade7645a, 1 | BRF_PRG | BRF_ESS }, // 13 + + { "ic8.inf", 0x2000, 0x4e3123b8, 2 | BRF_PRG | BRF_ESS }, // 14 M6800 Code + + { "ic57.inf", 0x2000, 0x65a4ef79, 6 | BRF_GRA }, // 15 Tilemap Tiles + { "ic58.inf", 0x2000, 0x4bb1c2a0, 6 | BRF_GRA }, // 16 + { "ic41.inf", 0x2000, 0xf3f7238f, 6 | BRF_GRA }, // 17 + + { "prom1", 0x0020, 0x85057e40, 0 | BRF_OPT }, // 18 n/c + { "prom2", 0x0100, 0xefb03024, 0 | BRF_OPT }, // 19 + { "prom3", 0x0200, 0x0ea3f7fb, 0 | BRF_OPT }, // 20 +}; + +STD_ROM_PICK(inferno) +STD_ROM_FN(inferno) + +static struct BurnRomInfo joust2RomDesc[] = { + { "cpu_2732_ic55_rom2_rev1.4c", 0x1000, 0x08b0d5bd, 1 | BRF_PRG | BRF_ESS }, // 0 M6809 Code (0xd000-0xdfff) + { "cpu_2732_ic9_rom3_rev2.4d", 0x1000, 0x951175ce, 1 | BRF_PRG | BRF_ESS }, // 1 (fixed window) + { "cpu_2732_ic10_rom4_rev2.4f", 0x1000, 0xba6e0f6c, 1 | BRF_PRG | BRF_ESS }, // 2 + + { "cpu_2732_ic18_rom11_rev1.8j", 0x2000, 0x9dc986f9, 1 | BRF_PRG | BRF_ESS }, // 3 (bank 0) + { "cpu_2732_ic16_rom9_rev2.8h", 0x2000, 0x56e2b550, 1 | BRF_PRG | BRF_ESS }, // 4 + { "cpu_2732_ic14_rom7_rev2.6j", 0x2000, 0xf3bce576, 1 | BRF_PRG | BRF_ESS }, // 5 + { "cpu_2732_ic12_rom5_rev2.6h", 0x2000, 0x5f8b4919, 1 | BRF_PRG | BRF_ESS }, // 6 + + { "cpu_2732_ic26_rom19_rev1.10j", 0x2000, 0x4ef5e805, 1 | BRF_PRG | BRF_ESS }, // 7 (bank 1) + { "cpu_2732_ic24_rom17_rev1.10h", 0x2000, 0x4861f063, 1 | BRF_PRG | BRF_ESS }, // 8 + { "cpu_2732_ic22_rom15_rev1.9j", 0x2000, 0x421aafa8, 1 | BRF_PRG | BRF_ESS }, // 9 + { "cpu_2732_ic20_rom13_rev1.9h", 0x2000, 0x3432ff55, 1 | BRF_PRG | BRF_ESS }, // 10 + + { "cpu_2732_ic17_rom10_rev1.8i", 0x2000, 0x3e01b597, 1 | BRF_PRG | BRF_ESS }, // 11 (bank 2, 3 of 4 chips) + { "cpu_2732_ic15_rom8_rev1.8g", 0x2000, 0xff26fb29, 1 | BRF_PRG | BRF_ESS }, // 12 + { "cpu_2732_ic13_rom6_rev2.6i", 0x2000, 0x5f107db5, 1 | BRF_PRG | BRF_ESS }, // 13 + + { "cpu_2732_ic25_rom18_rev1.10i", 0x2000, 0x47580af5, 1 | BRF_PRG | BRF_ESS }, // 14 (bank 3) + { "cpu_2732_ic23_rom16_rev1.10g", 0x2000, 0x869b5942, 1 | BRF_PRG | BRF_ESS }, // 15 + { "cpu_2732_ic21_rom14_rev1.9i", 0x2000, 0x0bbd867c, 1 | BRF_PRG | BRF_ESS }, // 16 + { "cpu_2732_ic19_rom12_rev1.9g", 0x2000, 0xb9221ed1, 1 | BRF_PRG | BRF_ESS }, // 17 + + { "cpu_2764_ic8_rom1_rev1.0f", 0x2000, 0x84517c3c, 2 | BRF_PRG | BRF_ESS }, // 18 M6800 Code + + { "vid_27128_ic57_rom20_rev1.8f", 0x4000, 0x572c6b01, 6 | BRF_GRA }, // 19 Tilemap Tiles + { "vid_27128_ic58_rom21_rev1.9f", 0x4000, 0xaa94bf05, 6 | BRF_GRA }, // 20 + { "vid_27128_ic41_rom22_rev1.9d", 0x4000, 0xc41e3daa, 6 | BRF_GRA }, // 21 + + { "vid_82s123_ic14_a-5282-10295.2b", 0x0020, 0x85057e40, 0 | BRF_OPT }, // 22 n/c + { "vid_82s129_ic47_a-5282-10294.15d", 0x0100, 0xefb03024, 0 | BRF_OPT }, // 23 + { "vid_82s147a_ic60_a-5282-10292.12f", 0x0200, 0x0ea3f7fb, 0 | BRF_OPT }, // 24 + + { "snd_27256_rom23_rev1.u4", 0x8000, 0x3af6b47d, 7 | BRF_SND | BRF_ESS }, // 25 BG sound board M6809 Code + { "snd_27256_rom24_rev1.u19", 0x8000, 0xe7f9ed2e, 7 | BRF_SND | BRF_ESS }, // 26 + { "snd_27256_rom25_rev1.u20", 0x8000, 0xc85b29f7, 7 | BRF_SND | BRF_ESS }, // 27 +}; + +STD_ROM_PICK(joust2) +STD_ROM_FN(joust2) + +static struct BurnRomInfo joust2r1RomDesc[] = { + { "cpu_2732_ic55_rom2_rev1.4c", 0x1000, 0x08b0d5bd, 1 | BRF_PRG | BRF_ESS }, // 0 M6809 Code (0xd000-0xdfff) + { "cpu_2732_ic9_rom3_rev1.4d", 0x1000, 0x6f319644, 1 | BRF_PRG | BRF_ESS }, // 1 (fixed window) + { "cpu_2732_ic10_rom4_rev1.4f", 0x1000, 0x027b9f0c, 1 | BRF_PRG | BRF_ESS }, // 2 + + { "cpu_2732_ic18_rom11_rev1.8j", 0x2000, 0x9dc986f9, 1 | BRF_PRG | BRF_ESS }, // 3 (bank 0) + { "cpu_2732_ic16_rom9_rev1.8h", 0x2000, 0x0c77e22f, 1 | BRF_PRG | BRF_ESS }, // 4 + { "cpu_2732_ic14_rom7_rev1.6j", 0x2000, 0xfb9455ca, 1 | BRF_PRG | BRF_ESS }, // 5 + { "cpu_2732_ic12_rom5_rev1.6h", 0x2000, 0x31248a0d, 1 | BRF_PRG | BRF_ESS }, // 6 + + { "cpu_2732_ic26_rom19_rev1.10j", 0x2000, 0x4ef5e805, 1 | BRF_PRG | BRF_ESS }, // 7 (bank 1) + { "cpu_2732_ic24_rom17_rev1.10h", 0x2000, 0x4861f063, 1 | BRF_PRG | BRF_ESS }, // 8 + { "cpu_2732_ic22_rom15_rev1.9j", 0x2000, 0x421aafa8, 1 | BRF_PRG | BRF_ESS }, // 9 + { "cpu_2732_ic20_rom13_rev1.9h", 0x2000, 0x3432ff55, 1 | BRF_PRG | BRF_ESS }, // 10 + + { "cpu_2732_ic17_rom10_rev1.8i", 0x2000, 0x3e01b597, 1 | BRF_PRG | BRF_ESS }, // 11 (bank 2, 3 of 4 chips) + { "cpu_2732_ic15_rom8_rev1.8g", 0x2000, 0xff26fb29, 1 | BRF_PRG | BRF_ESS }, // 12 + { "cpu_2732_ic13_rom6_rev1.6i", 0x2000, 0x6a8c87d7, 1 | BRF_PRG | BRF_ESS }, // 13 + + { "cpu_2732_ic25_rom18_rev1.10i", 0x2000, 0x47580af5, 1 | BRF_PRG | BRF_ESS }, // 14 (bank 3) + { "cpu_2732_ic23_rom16_rev1.10g", 0x2000, 0x869b5942, 1 | BRF_PRG | BRF_ESS }, // 15 + { "cpu_2732_ic21_rom14_rev1.9i", 0x2000, 0x0bbd867c, 1 | BRF_PRG | BRF_ESS }, // 16 + { "cpu_2732_ic19_rom12_rev1.9g", 0x2000, 0xb9221ed1, 1 | BRF_PRG | BRF_ESS }, // 17 + + { "cpu_2764_ic8_rom1_rev1.0f", 0x2000, 0x84517c3c, 2 | BRF_PRG | BRF_ESS }, // 18 M6800 Code + + { "vid_27128_ic57_rom20_rev1.8f", 0x4000, 0x572c6b01, 6 | BRF_GRA }, // 19 Tilemap Tiles + { "vid_27128_ic58_rom21_rev1.9f", 0x4000, 0xaa94bf05, 6 | BRF_GRA }, // 20 + { "vid_27128_ic41_rom22_rev1.9d", 0x4000, 0xc41e3daa, 6 | BRF_GRA }, // 21 + + { "vid_82s123_ic14_a-5282-10295.2b", 0x0020, 0x85057e40, 0 | BRF_OPT }, // 22 n/c + { "vid_82s129_ic47_a-5282-10294.15d", 0x0100, 0xefb03024, 0 | BRF_OPT }, // 23 + { "vid_82s147a_ic60_a-5282-10292.12f", 0x0200, 0x0ea3f7fb, 0 | BRF_OPT }, // 24 + + { "snd_27256_rom23_rev1.u4", 0x8000, 0x3af6b47d, 7 | BRF_SND | BRF_ESS }, // 25 BG sound board M6809 Code + { "snd_27256_rom24_rev1.u19", 0x8000, 0xe7f9ed2e, 7 | BRF_SND | BRF_ESS }, // 26 + { "snd_27256_rom25_rev1.u20", 0x8000, 0xc85b29f7, 7 | BRF_SND | BRF_ESS }, // 27 +}; + +STD_ROM_PICK(joust2r1) +STD_ROM_FN(joust2r1) + +static struct BurnRomInfo tshootRomDesc[] = { + { "rom18.ic55", 0x1000, 0xeffc33f1, 1 | BRF_PRG | BRF_ESS }, // 0 M6809 Code (0xd000-0xdfff) + { "rom2.ic9", 0x1000, 0xfd982687, 1 | BRF_PRG | BRF_ESS }, // 1 (fixed window) + { "rom3.ic10", 0x1000, 0x9617054d, 1 | BRF_PRG | BRF_ESS }, // 2 + + { "rom11.ic18", 0x2000, 0x60d5fab8, 1 | BRF_PRG | BRF_ESS }, // 3 (bank 0) + { "rom9.ic16", 0x2000, 0xa4dd4a0e, 1 | BRF_PRG | BRF_ESS }, // 4 + { "rom7.ic14", 0x2000, 0xf25505e6, 1 | BRF_PRG | BRF_ESS }, // 5 + { "rom5.ic12", 0x2000, 0x94a7c0ed, 1 | BRF_PRG | BRF_ESS }, // 6 + + { "rom17.ic26", 0x2000, 0xb02d1ccd, 1 | BRF_PRG | BRF_ESS }, // 7 (bank 1, 2 of 4 chips) + { "rom15.ic24", 0x2000, 0x11709935, 1 | BRF_PRG | BRF_ESS }, // 8 + + { "rom10.ic17", 0x2000, 0x0f32bad8, 1 | BRF_PRG | BRF_ESS }, // 9 (bank 2) + { "rom8.ic15", 0x2000, 0xe9b6cbf7, 1 | BRF_PRG | BRF_ESS }, // 10 + { "rom6.ic13", 0x2000, 0xa49f617f, 1 | BRF_PRG | BRF_ESS }, // 11 + { "rom4.ic11", 0x2000, 0xb026dc00, 1 | BRF_PRG | BRF_ESS }, // 12 + + { "rom16.ic25", 0x2000, 0x69ce38f8, 1 | BRF_PRG | BRF_ESS }, // 13 (bank 3) + { "rom14.ic23", 0x2000, 0x769a4ae5, 1 | BRF_PRG | BRF_ESS }, // 14 + { "rom13.ic21", 0x2000, 0xec016c9b, 1 | BRF_PRG | BRF_ESS }, // 15 + { "rom12.ic19", 0x2000, 0x98ae7afa, 1 | BRF_PRG | BRF_ESS }, // 16 + + { "rom1.ic8", 0x2000, 0x011a94a7, 2 | BRF_PRG | BRF_ESS }, // 17 M6800 Code + + { "rom20.ic57", 0x2000, 0xc6e1d253, 6 | BRF_GRA }, // 18 Tilemap Tiles + { "rom21.ic58", 0x2000, 0x9874e90f, 6 | BRF_GRA }, // 19 + { "rom19.ic41", 0x2000, 0xb9ce4d2a, 6 | BRF_GRA }, // 20 + + { "82s123.ic14a", 0x0020, 0x27a6d555, 0 | BRF_OPT }, // 21 n/c + { "82s129.ic47", 0x0100, 0xefb03024, 0 | BRF_OPT }, // 22 + { "7649.ic60", 0x0200, 0x0ea3f7fb, 0 | BRF_OPT }, // 23 +}; + +STD_ROM_PICK(tshoot) +STD_ROM_FN(tshoot) + +static INT32 TshootInit() +{ + is_tshoot = 1; + + return DrvInit(3, 3, 0, -1, 0x9000); +} + +struct BurnDriver BurnDrvTshoot = { + "tshoot", NULL, NULL, NULL, "1984", + "Turkey Shoot\0", NULL, "Williams", "6809 System", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING, 2, HARDWARE_MISC_PRE90S, GBF_SHOOT, 0, + NULL, tshootRomInfo, tshootRomName, NULL, NULL, NULL, NULL, TshootInputInfo, NULL, + TshootInit, DrvExit, DrvFrame, Williams2Draw, DrvScan, &DrvRecalc, 0x410, + 276, 240, 4, 3 +}; + +static struct BurnRomInfo mysticmRomDesc[] = { + { "mm02_2.a09", 0x1000, 0x3a776ea8, 1 | BRF_PRG | BRF_ESS }, // 0 M6809 Code (fixed window) + { "mm03_2.a10", 0x1000, 0x6e247c75, 1 | BRF_PRG | BRF_ESS }, // 1 + + { "mm11_1.a18", 0x2000, 0xf537968e, 1 | BRF_PRG | BRF_ESS }, // 2 (bank 0) + { "mm09_1.a16", 0x2000, 0x3bd12f6c, 1 | BRF_PRG | BRF_ESS }, // 3 + { "mm07_1.a14", 0x2000, 0xea2a2a68, 1 | BRF_PRG | BRF_ESS }, // 4 + { "mm05_1.a12", 0x2000, 0xb514eef3, 1 | BRF_PRG | BRF_ESS }, // 5 + + { "mm18_1.a26", 0x2000, 0x9b391a81, 1 | BRF_PRG | BRF_ESS }, // 6 (bank 1, 3 of 4 chips) + { "mm16_1.a24", 0x2000, 0x399e175d, 1 | BRF_PRG | BRF_ESS }, // 7 + { "mm14_1.a22", 0x2000, 0x191153b1, 1 | BRF_PRG | BRF_ESS }, // 8 + + { "mm10_1.a17", 0x2000, 0xd6a37509, 1 | BRF_PRG | BRF_ESS }, // 9 (bank 2) + { "mm08_1.a15", 0x2000, 0x6f1a64f2, 1 | BRF_PRG | BRF_ESS }, // 10 + { "mm06_1.a13", 0x2000, 0x2e6795d4, 1 | BRF_PRG | BRF_ESS }, // 11 + { "mm04_1.a11", 0x2000, 0xc222fb64, 1 | BRF_PRG | BRF_ESS }, // 12 + + { "mm17_1.a25", 0x2000, 0xd36f0a96, 1 | BRF_PRG | BRF_ESS }, // 13 (bank 3) + { "mm15_1.a23", 0x2000, 0xcd5d99da, 1 | BRF_PRG | BRF_ESS }, // 14 + { "mm13_1.a21", 0x2000, 0xef4b79db, 1 | BRF_PRG | BRF_ESS }, // 15 + { "mm12_1.a19", 0x2000, 0xa1f04bf0, 1 | BRF_PRG | BRF_ESS }, // 16 + + { "mm01_1.a08", 0x2000, 0x65339512, 2 | BRF_PRG | BRF_ESS }, // 17 M6800 Code + + { "mm20_1.b57", 0x2000, 0x5c0f4f46, 6 | BRF_GRA }, // 18 Tilemap Tiles + { "mm21_1.b58", 0x2000, 0xcb90b3c5, 6 | BRF_GRA }, // 19 + { "mm19_1.b41", 0x2000, 0xe274df86, 6 | BRF_GRA }, // 20 + + { "ic14.bpr", 0x0020, 0x27a6d555, 0 | BRF_OPT }, // 21 n/c + { "ic47.bpr", 0x0100, 0xefb03024, 0 | BRF_OPT }, // 22 + { "ic60.bpr", 0x0200, 0x0ea3f7fb, 0 | BRF_OPT }, // 23 +}; + +STD_ROM_PICK(mysticm) +STD_ROM_FN(mysticm) + +static struct BurnRomInfo mysticmpRomDesc[] = { + { "cpu_2732_ic9_rom2_proto6.d4", 0x1000, 0x3e4f53dd, 1 | BRF_PRG | BRF_ESS }, // 0 M6809 Code (fixed window) + { "cpu_2732_ic10_rom3_proto6.f4", 0x1000, 0x6a25ee4b, 1 | BRF_PRG | BRF_ESS }, // 1 + + { "cpu_2764_ic18_rom11_proto5.j8", 0x2000, 0xf537968e, 1 | BRF_PRG | BRF_ESS }, // 2 (bank 0) + { "cpu_2764_ic16_rom9_proto5.h8", 0x2000, 0x3bd12f6c, 1 | BRF_PRG | BRF_ESS }, // 3 + { "cpu_2764_ic14_rom7_proto5.j6", 0x2000, 0xea2a2a68, 1 | BRF_PRG | BRF_ESS }, // 4 + { "cpu_2764_ic12_rom5_proto5.h6", 0x2000, 0xb514eef3, 1 | BRF_PRG | BRF_ESS }, // 5 + + { "cpu_2764_ic26_rom18_proto5.j10", 0x2000, 0x9b391a81, 1 | BRF_PRG | BRF_ESS }, // 6 (bank 1, 3 of 4 chips) + { "cpu_2764_ic24_rom16_proto5.h10", 0x2000, 0x399e175d, 1 | BRF_PRG | BRF_ESS }, // 7 + { "cpu_2764_ic22_rom14_proto5.j9", 0x2000, 0x191153b1, 1 | BRF_PRG | BRF_ESS }, // 8 + + { "cpu_2764_ic17_rom10_proto4.i8", 0x2000, 0xd6a37509, 1 | BRF_PRG | BRF_ESS }, // 9 (bank 2) + { "cpu_2764_ic15_rom8_proto4.g8", 0x2000, 0x6f1a64f2, 1 | BRF_PRG | BRF_ESS }, // 10 + { "cpu_2764_ic13_rom6_proto4.i6", 0x2000, 0x2e6795d4, 1 | BRF_PRG | BRF_ESS }, // 11 + { "cpu_2764_ic11_rom4_proto4.g6", 0x2000, 0xc222fb64, 1 | BRF_PRG | BRF_ESS }, // 12 + + { "cpu_2764_ic25_rom17_proto6.i10", 0x2000, 0x7acc9995, 1 | BRF_PRG | BRF_ESS }, // 13 (bank 3) + { "cpu_2764_ic23_rom15_proto6.g10", 0x2000, 0xc32d1ce5, 1 | BRF_PRG | BRF_ESS }, // 14 + { "cpu_2764_ic21_rom13_proto6.i9", 0x2000, 0xe387a785, 1 | BRF_PRG | BRF_ESS }, // 15 + { "cpu_2764_ic19_rom12_proto6.g9", 0x2000, 0xa1f04bf0, 1 | BRF_PRG | BRF_ESS }, // 16 + + { "cpu_2764_ic8_rom1_proto4.f0", 0x2000, 0x65339512, 2 | BRF_PRG | BRF_ESS }, // 17 M6800 Code + + { "ram_2764_ic57_rom20_rev1.f9", 0x2000, 0x5c0f4f46, 6 | BRF_GRA }, // 18 Tilemap Tiles + { "ram_2764_ic58_rom21_rev1.f10", 0x2000, 0xcb90b3c5, 6 | BRF_GRA }, // 19 + { "ram_2764_ic41_rom19_rev1.d10", 0x2000, 0xe274df86, 6 | BRF_GRA }, // 20 +}; + +STD_ROM_PICK(mysticmp) +STD_ROM_FN(mysticmp) + +static INT32 MysticmInit() +{ + is_mysticm = 1; + + return DrvInit(3, 9, 0, -1, 0x9000); +} + +struct BurnDriver BurnDrvMysticm = { + "mysticm", NULL, NULL, NULL, "1983", + "Mystic Marathon\0", "Bad Colors", "Williams", "6809 System", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING, 2, HARDWARE_MISC_PRE90S, GBF_PLATFORM, 0, + NULL, mysticmRomInfo, mysticmRomName, NULL, NULL, NULL, NULL, MysticmInputInfo, NULL, + MysticmInit, DrvExit, DrvFrame, Williams2Draw, DrvScan, &DrvRecalc, 0x410, + 276, 240, 4, 3 +}; + +struct BurnDriver BurnDrvMysticmp = { + "mysticmp", "mysticm", NULL, NULL, "1983", + "Mystic Marathon (prototype)\0", "Bad Colors", "Williams", "6809 System", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE, 2, HARDWARE_MISC_PRE90S, GBF_PLATFORM, 0, + NULL, mysticmpRomInfo, mysticmpRomName, NULL, NULL, NULL, NULL, MysticmInputInfo, NULL, + MysticmInit, DrvExit, DrvFrame, Williams2Draw, DrvScan, &DrvRecalc, 0x410, + 276, 240, 4, 3 +}; + +static INT32 Joust2Init() +{ + is_joust2 = 1; + has_s11_bg = 1; + + return DrvInit(3, 8, 0, -1, 0x9000); +} + +struct BurnDriver BurnDrvJoust2 = { + "joust2", NULL, NULL, NULL, "1986", + "Joust 2 - Survival of the Fittest (revision 2)\0", "S11 background sound board not emulated", "Williams", "6809 System", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_ORIENTATION_VERTICAL, 2, HARDWARE_MISC_PRE90S, GBF_SHOOT, 0, + NULL, joust2RomInfo, joust2RomName, NULL, NULL, NULL, NULL, Joust2InputInfo, NULL, + Joust2Init, DrvExit, DrvFrame, Williams2Draw, DrvScan, &DrvRecalc, 0x410, + 240, 276, 3, 4 +}; + +struct BurnDriver BurnDrvJoust2r1 = { + "joust2r1", "joust2", NULL, NULL, "1986", + "Joust 2 - Survival of the Fittest (revision 1)\0", "S11 background sound board not emulated", "Williams", "6809 System", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_ORIENTATION_VERTICAL | BDF_CLONE, 2, HARDWARE_MISC_PRE90S, GBF_SHOOT, 0, + NULL, joust2r1RomInfo, joust2r1RomName, NULL, NULL, NULL, NULL, Joust2InputInfo, NULL, + Joust2Init, DrvExit, DrvFrame, Williams2Draw, DrvScan, &DrvRecalc, 0x410, + 240, 276, 3, 4 +}; + +static INT32 InfernoInit() +{ + return DrvInit(3, 7, 0, -1, 0x9000); +} + +struct BurnDriver BurnDrvInferno = { + "inferno", NULL, NULL, NULL, "1984", + "Inferno (Williams)\0", NULL, "Williams", "6809 System", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING, 2, HARDWARE_MISC_PRE90S, GBF_SHOOT, 0, + NULL, infernoRomInfo, infernoRomName, NULL, NULL, NULL, NULL, InfernoInputInfo, NULL, + InfernoInit, DrvExit, DrvFrame, Williams2Draw, DrvScan, &DrvRecalc, 0x410, + 276, 240, 4, 3 +}; + + // Robotron: 2084 (Solid Blue label) /* "Release 5" Solid Blue labels, "B" type ROMs labeled 3005-13 through 3005-24 */ @@ -2757,7 +4083,7 @@ struct BurnDriver BurnDrvRobotron = { // Robotron: 2084 (Yellow/Orange label) /* "Release 4" Yellow label / Red stripe & Black print or Yellow label / Red stripe & Green print "B" type ROMs numbered 3005-13 through 3005-24 */ - + static struct BurnRomInfo robotronyoRomDesc[] = { { "2084_rom_10b_3005-10.a7", 0x1000, 0x4a9d5f52, 1 | BRF_PRG | BRF_ESS }, // 0 M6809 Code { "2084_rom_11b_3005-11.c7", 0x1000, 0x2afc5e7f, 1 | BRF_PRG | BRF_ESS }, // 1 @@ -2794,7 +4120,7 @@ struct BurnDriver BurnDrvRobotronyo = { // Robotron: 2084 (Release 3) /* "Release 3" Censored prototype seen in the Robotron promo videotape, recovered from the Vid Kidz dev system */ - + static struct BurnRomInfo robotronr3RomDesc[] = { { "r3_10.a7", 0x1000, 0x1fb75828, 1 | BRF_PRG | BRF_ESS }, // 0 M6809 Code { "r3_11.c7", 0x1000, 0xccfaf7da, 1 | BRF_PRG | BRF_ESS }, // 1 @@ -3884,6 +5210,23 @@ STD_ROM_FN(blaster) static INT32 BlasterInit() { blaster = 1; + is_blasterkit = 0; + + INT32 nRet = DrvInit(2, 0, 6, 2, 0x9700); + + if (nRet == 0) + { + pStartDraw = DrvDrawBegin; + pDrawScanline = BlasterDrawLine; + } + + return nRet; +} + +static INT32 BlasterkitInit() +{ + blaster = 1; + is_blasterkit = 1; INT32 nRet = DrvInit(2, 0, 6, 2, 0x9700); @@ -3944,8 +5287,8 @@ struct BurnDriver BurnDrvBlasterkit = { "Blaster (conversion kit)\0", NULL, "Williams / Vid Kidz", "6809 System", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_CLONE, 2, HARDWARE_MISC_PRE90S, GBF_SHOOT, 0, - NULL, blasterkitRomInfo, blasterkitRomName, NULL, NULL, NULL, NULL, BlasterInputInfo, NULL, - BlasterInit, DrvExit, DrvFrame, BlasterDraw, DrvScan, &DrvRecalc, 0x110, + NULL, blasterkitRomInfo, blasterkitRomName, NULL, NULL, NULL, NULL, BlasterkitInputInfo, NULL, + BlasterkitInit, DrvExit, DrvFrame, BlasterDraw, DrvScan, &DrvRecalc, 0x110, 292, 240, 4, 3 }; From 565c0d153dcc6bc5284ea50525a446d2f21b46db Mon Sep 17 00:00:00 2001 From: Nelfe80 <7347093+Nelfe80@users.noreply.github.com> Date: Tue, 27 Oct 2026 16:57:48 +0100 Subject: [PATCH 17/26] multipcm: keep the slot pointers out of save states (#2838) MultiPCMScan saved the whole chip struct, including the slot pointers (sample, LFO table and scale). They are only valid in the process that set them, so a state loaded by another process (RetroArch netplay sends the host state to the guest) crashed on the first sample played by a slot. This hits the Multi 32 games (Title Fight, Hard Dunk, OutRunners, Stadium Cross). The pointers are now cleared in the state and rebuilt from the slot registers after the scan, the way WriteSlot sets them. The state size and layout do not change, older states load, and the same emulated state gives the same bytes in every process. Co-authored-by: Claude Opus 5.5 --- src/burn/snd/multipcm.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/burn/snd/multipcm.cpp b/src/burn/snd/multipcm.cpp index 2f77e9ee5..86e069f60 100644 --- a/src/burn/snd/multipcm.cpp +++ b/src/burn/snd/multipcm.cpp @@ -466,9 +466,40 @@ void MultiPCMExit() } } +// The slot pointers (sample, LFO tables) are only valid in the process that set +// them, a state loaded in another one (netplay) would crash. They are kept out of +// the state and rebuilt from the slot registers, as WriteSlot sets them: reg 1 +// selects the sample, regs 6 and 7 the LFO scales (an LFO is only stepped when +// its depth, reg & 7, is not zero). +static void ClearSlotPointers() +{ + for (INT32 i = 0; i < 28; i++) { + SLOT *slot = &chip.Slots[i]; + + slot->Sample = NULL; + slot->PLFO.table = slot->PLFO.scale = NULL; + slot->ALFO.table = slot->ALFO.scale = NULL; + } +} + +static void RebuildSlotPointers() +{ + for (INT32 i = 0; i < 28; i++) { + SLOT *slot = &chip.Slots[i]; + + slot->Sample = chip.Samples + slot->Regs[1]; + slot->PLFO.table = PLFO_TRI; + slot->PLFO.scale = PSCALES[slot->Regs[6] & 7]; + slot->ALFO.table = ALFO_TRI; + slot->ALFO.scale = ASCALES[slot->Regs[7] & 7]; + } +} + void MultiPCMScan(INT32 nAction, INT32 *pnMin) { + ClearSlotPointers(); SCAN_VAR(chip); + RebuildSlotPointers(); } void MultiPCMReset() From d08cae11b7de5b8e982434ef0b1a279fb112b128 Mon Sep 17 00:00:00 2001 From: Steve Gordon Date: Tue, 27 Oct 2026 17:32:24 -0700 Subject: [PATCH 18/26] Add Strider EX and Final Fight EX; update Vampire Savior Arrange to RC4 (#2840) * Vampire Savior Arrange: update both regions to RC4 Refresh program, graphics and extended sound ROM checksums from the published 2026-10-27 kit. Preserve the existing CPS2+ layout, initialization and set names. Public kit: https://arcadepatches.com/downloads/vsav-arrange-rc4-kit.zip SHA256: 75ad38e664a9c96acd50ceac37a8b5e050a9444abb131aa8a98e561b2f08a641 * Strider EX: add USA and Japan RC1 sets Register the published 2 MiB programs and 8 MiB graphics. Add the key-defined extended mapper without changing stock maps; derive the star ROM index after the added program member. Preserve the USA 10 MHz and Japan 12 MHz initialization and existing controls. Verified complete published member SHA256 maps, compiled ROM inventories, loaded program byte order and star sources; checked all 655360 mapper type/code combinations against the qualified control key with a stock negative control. Both regions passed headless AddressSanitizer 5400-frame boot/input and save/load runs. Exact CPS1 state replay is outside this bounded smoke. * Final Fight EX: add USA and Japan CPS1 RC4 sets Register the published 2 MiB programs and 6 MiB graphics, using the qualified two-bank 4+2 MiB mapper. Keep the stock 10 MHz initialization and Final Fight controls; stock mappings are unchanged. Verified all 22 qualified input members per region, all 21 compiled game-ROM entries, complete loaded program byte order, video-register ABI and all 655360 mapper type/code combinations with a stock negative control. Both regions passed headless AddressSanitizer 5400-frame boot/input and save/load runs. Existing generated kit outputs were read back from MiSTer without modifying it. --- src/burn/drv/capcom/cps.h | 2 + src/burn/drv/capcom/cps_config.cpp | 42 +++++++ src/burn/drv/capcom/d_cps1.cpp | 174 ++++++++++++++++++++++++++ src/burn/drv/capcom/d_cps2_custom.cpp | 64 +++++----- 4 files changed, 250 insertions(+), 32 deletions(-) diff --git a/src/burn/drv/capcom/cps.h b/src/burn/drv/capcom/cps.h index c580f7409..40e9ef3f3 100644 --- a/src/burn/drv/capcom/cps.h +++ b/src/burn/drv/capcom/cps.h @@ -181,6 +181,8 @@ INT32 Cps2LoadTilesGigaman2(UINT8 *Tile, UINT8 *pSrc); #define mapper_pang3b4 48 #define mapper_TKSGZB 49 #define mapper_varthb2 50 +#define mapper_STRIDEREX 51 +#define mapper_FFIGHTEX 52 extern void SetGfxMapper(INT32 MapperId); extern INT32 GfxRomBankMapper(INT32 Type, INT32 Code); extern void SetCpsBId(INT32 CpsBId, INT32 bStars); diff --git a/src/burn/drv/capcom/cps_config.cpp b/src/burn/drv/capcom/cps_config.cpp index 7fadf6b28..40cef49d1 100644 --- a/src/burn/drv/capcom/cps_config.cpp +++ b/src/burn/drv/capcom/cps_config.cpp @@ -72,6 +72,19 @@ static const struct GfxRange mapper_ST24M1_table[] = { 0 , 0, 0, 0 } }; +// Strider EX: original ST24M1 ranges plus the native backport graphics bank. +static const struct GfxRange mapper_STRIDEREX_table[] = { + { GFXTYPE_STARS, 0x00000, 0x003ff, 0 }, + { GFXTYPE_SPRITES, 0x00000, 0x04fff, 0 }, + { GFXTYPE_SCROLL2, 0x04000, 0x07fff, 0 }, + { GFXTYPE_SCROLL3, 0x00000, 0x07fff, 1 }, + { GFXTYPE_SCROLL1, 0x07000, 0x07fff, 1 }, + { GFXTYPE_SPRITES | GFXTYPE_SCROLL2, 0x10000, 0x17fff, 2 }, + { GFXTYPE_SCROLL1, 0x08000, 0x0bfff, 2 }, + { GFXTYPE_SCROLL3, 0x1c000, 0x1ffff, 2 }, + { 0, 0, 0, 0 } +}; + static const struct GfxRange mapper_ST22B_table[] = { { GFXTYPE_STARS, 0x00000, 0x1ffff, 0 }, { GFXTYPE_SPRITES, 0x00000, 0x03fff, 0 }, @@ -107,6 +120,17 @@ static const struct GfxRange mapper_WL24B_table[] = { { 0 , 0, 0, 0 } }; +// Final Fight EX: published 6 MiB (4+2) graphics mapper. +static const struct GfxRange mapper_FFIGHTEX_table[] = { + { GFXTYPE_SPRITES, 0x00000, 0x043ff, 0 }, + { GFXTYPE_SCROLL1, 0x04400, 0x04bff, 0 }, + { GFXTYPE_SCROLL3, 0x04c00, 0x05fff, 0 }, + { GFXTYPE_SCROLL2, 0x06000, 0x07fff, 0 }, + { GFXTYPE_SPRITES | GFXTYPE_SCROLL1 | GFXTYPE_SCROLL2 | GFXTYPE_SCROLL3, 0x08000, 0x0ffff, 0 }, + { GFXTYPE_SPRITES | GFXTYPE_SCROLL1 | GFXTYPE_SCROLL2 | GFXTYPE_SCROLL3, 0x10000, 0x17fff, 1 }, + { 0, 0, 0, 0 } +}; + static const struct GfxRange mapper_S224B_table[] = { { GFXTYPE_SPRITES, 0x0000, 0x43ff, 0 }, { GFXTYPE_SCROLL1, 0x4400, 0x4bff, 0 }, @@ -542,6 +566,24 @@ void SetGfxMapper(INT32 MapperId) return; } + case mapper_FFIGHTEX: { + GfxBankSizes[0] = 0x10000; + GfxBankSizes[1] = 0x08000; + GfxBankSizes[2] = 0x00000; + GfxBankSizes[3] = 0x00000; + GfxBankMapper = mapper_FFIGHTEX_table; + return; + } + + case mapper_STRIDEREX: { + GfxBankSizes[0] = 0x08000; + GfxBankSizes[1] = 0x08000; + GfxBankSizes[2] = 0x10000; + GfxBankSizes[3] = 0x00000; + GfxBankMapper = mapper_STRIDEREX_table; + return; + } + case mapper_ST24M1: { GfxBankSizes[0] = 0x8000; GfxBankSizes[1] = 0x8000; diff --git a/src/burn/drv/capcom/d_cps1.cpp b/src/burn/drv/capcom/d_cps1.cpp index c8acee1eb..a67e982aa 100644 --- a/src/burn/drv/capcom/d_cps1.cpp +++ b/src/burn/drv/capcom/d_cps1.cpp @@ -7348,6 +7348,62 @@ static struct BurnRomInfo DynwarjrRomDesc[] = { STD_ROM_PICK(Dynwarjr) STD_ROM_FN(Dynwarjr) +// https://arcadepatches.com/projects/final-fight-ex/ (RC4) +static struct BurnRomInfo Ffightus01RomDesc[] = { + { "c07.p1", 0x020000, 0x1bbe6639, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_BYTESWAP }, + { "c07.p2", 0x020000, 0x9a9fe4f3, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_BYTESWAP }, + { "c07.p3", 0x020000, 0xd3c9e580, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_BYTESWAP }, + { "c07u.p4", 0x020000, 0x55a29283, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_BYTESWAP }, + { "c07.p5", 0x080000, 0x1c9b1b36, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_NO_BYTESWAP }, + { "c07us01.p7", 0x100000, 0xe6e4df5f, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_NO_BYTESWAP }, + { "c07.c01", 0x080000, 0x7b4aad16, BRF_GRA | CPS1_TILES }, + { "c07.c03", 0x080000, 0x28192faf, BRF_GRA | CPS1_TILES }, + { "c07.c05", 0x080000, 0xdeff8c10, BRF_GRA | CPS1_TILES }, + { "c07.c07", 0x080000, 0x4b532b36, BRF_GRA | CPS1_TILES }, + { "c07us01.c09", 0x080000, 0x3365bae7, BRF_GRA | CPS1_TILES }, + { "c07us01.c11", 0x080000, 0x9f53c8e0, BRF_GRA | CPS1_TILES }, + { "c07us01.c13", 0x080000, 0xcf35f998, BRF_GRA | CPS1_TILES }, + { "c07us01.c15", 0x080000, 0xd65f25ed, BRF_GRA | CPS1_TILES }, + { "c07us01.c17", 0x080000, 0xc9dbe31e, BRF_GRA | CPS1_TILES }, + { "c07us01.c19", 0x080000, 0x30c802e6, BRF_GRA | CPS1_TILES }, + { "c07us01.c21", 0x080000, 0x936c49c4, BRF_GRA | CPS1_TILES }, + { "c07us01.c23", 0x080000, 0xce2427a3, BRF_GRA | CPS1_TILES }, + { "c07.m1", 0x010000, 0x87eca9ec, BRF_PRG | CPS1_Z80_PROGRAM }, + { "c07.v1", 0x020000, 0x375c66e7, BRF_SND | CPS1_OKIM6295_SAMPLES }, + { "c07.v2", 0x020000, 0x1ef137f9, BRF_SND | CPS1_OKIM6295_SAMPLES }, +}; + +STD_ROM_PICK(Ffightus01) +STD_ROM_FN(Ffightus01) + +// https://arcadepatches.com/projects/final-fight-ex/ (RC4) +static struct BurnRomInfo Ffightjs01RomDesc[] = { + { "c07.p1", 0x020000, 0x4dcd780e, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_BYTESWAP }, + { "c07.p2", 0x020000, 0xfd0623ca, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_BYTESWAP }, + { "c07.p3", 0x020000, 0x4d30793c, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_BYTESWAP }, + { "c07j.p4", 0x020000, 0xdabe8850, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_BYTESWAP }, + { "c07.p5", 0x080000, 0xf9e7b052, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_NO_BYTESWAP }, + { "c07js01.p7", 0x100000, 0x8bdfd316, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_NO_BYTESWAP }, + { "c07.c01", 0x080000, 0x976bcfbb, BRF_GRA | CPS1_TILES }, + { "c07.c03", 0x080000, 0xcfeb60c6, BRF_GRA | CPS1_TILES }, + { "c07.c05", 0x080000, 0x32deeebd, BRF_GRA | CPS1_TILES }, + { "c07.c07", 0x080000, 0xaca1645f, BRF_GRA | CPS1_TILES }, + { "c07js01.c09", 0x080000, 0x923bfeba, BRF_GRA | CPS1_TILES }, + { "c07js01.c11", 0x080000, 0x2699067e, BRF_GRA | CPS1_TILES }, + { "c07js01.c13", 0x080000, 0xed5833cb, BRF_GRA | CPS1_TILES }, + { "c07js01.c15", 0x080000, 0xbed965c8, BRF_GRA | CPS1_TILES }, + { "c07js01.c17", 0x080000, 0x7d0a63fa, BRF_GRA | CPS1_TILES }, + { "c07js01.c19", 0x080000, 0xb61bca08, BRF_GRA | CPS1_TILES }, + { "c07js01.c21", 0x080000, 0x8e33931b, BRF_GRA | CPS1_TILES }, + { "c07js01.c23", 0x080000, 0xd60af261, BRF_GRA | CPS1_TILES }, + { "c07.m1", 0x010000, 0x57a4cf41, BRF_PRG | CPS1_Z80_PROGRAM }, + { "c07.v1", 0x020000, 0x375c66e7, BRF_SND | CPS1_OKIM6295_SAMPLES }, + { "c07.v2", 0x020000, 0x1ef137f9, BRF_SND | CPS1_OKIM6295_SAMPLES }, +}; + +STD_ROM_PICK(Ffightjs01) +STD_ROM_FN(Ffightjs01) + static struct BurnRomInfo FfightRomDesc[] = { { "ff_36.11f", 0x020000, 0xf9a5ce83, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_BYTESWAP }, { "ff_42.11h", 0x020000, 0x65f11215, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_BYTESWAP }, @@ -16387,6 +16443,57 @@ static struct BurnRomInfo StriderfixRomDesc[] = { STD_ROM_PICK(Striderfix) STD_ROM_FN(Striderfix) +static struct BurnRomInfo Striderus01RomDesc[] = { + { "c03us01.p1", 0x020000, 0x457395b9, CPS1_68K_PROGRAM_BYTESWAP | BRF_ESS | BRF_PRG }, + { "c03us01.p2", 0x020000, 0x118f8690, CPS1_68K_PROGRAM_BYTESWAP | BRF_ESS | BRF_PRG }, + { "c03us01.p3", 0x020000, 0x3306fd0f, CPS1_68K_PROGRAM_BYTESWAP | BRF_ESS | BRF_PRG }, + { "c03us01.p4", 0x020000, 0x1ac8cad9, CPS1_68K_PROGRAM_BYTESWAP | BRF_ESS | BRF_PRG }, + { "c03us01.p5", 0x080000, 0x06edfbaf, CPS1_68K_PROGRAM_NO_BYTESWAP | BRF_ESS | BRF_PRG }, + { "c03us01.p6", 0x100000, 0x075dfc39, CPS1_68K_PROGRAM_NO_BYTESWAP | BRF_ESS | BRF_PRG }, + { "c03us01.c01", 0x080000, 0xf92cc010, CPS1_TILES | BRF_GRA }, + { "c03us01.c03", 0x080000, 0x9abd7b1e, CPS1_TILES | BRF_GRA }, + { "c03us01.c05", 0x080000, 0x085ec243, CPS1_TILES | BRF_GRA }, + { "c03us01.c07", 0x080000, 0x2443df27, CPS1_TILES | BRF_GRA }, + { "c03.c17", 0x080000, 0x005f000b, CPS1_TILES | BRF_GRA }, + { "c03.c19", 0x080000, 0xb9441519, CPS1_TILES | BRF_GRA }, + { "c03.c21", 0x080000, 0xb7d04e8b, CPS1_TILES | BRF_GRA }, + { "c03.c23", 0x080000, 0x6b4713b4, CPS1_TILES | BRF_GRA }, + { "c03us01.c25", 0x100000, 0xe8507e5b, CPS1_TILES | BRF_GRA }, + { "c03us01.c27", 0x100000, 0x1120f48d, CPS1_TILES | BRF_GRA }, + { "c03us01.c29", 0x100000, 0xf7a36cd4, CPS1_TILES | BRF_GRA }, + { "c03us01.c31", 0x100000, 0xb0ef929a, CPS1_TILES | BRF_GRA }, + { "c03us01.m1", 0x010000, 0xa08e24b9, CPS1_Z80_PROGRAM | BRF_ESS | BRF_PRG }, + { "c03us01.v1", 0x020000, 0x897269dd, CPS1_OKIM6295_SAMPLES | BRF_SND }, + { "c03us01.v2", 0x020000, 0x3f56c8cc, CPS1_OKIM6295_SAMPLES | BRF_SND }, +}; + +STD_ROM_PICK(Striderus01) +STD_ROM_FN(Striderus01) + +static struct BurnRomInfo Striderjs01RomDesc[] = { + { "c03js01.p1", 0x080000, 0x39260f46, CPS1_68K_PROGRAM_NO_BYTESWAP | BRF_ESS | BRF_PRG }, + { "c03js01.p5", 0x080000, 0xabc6d8ff, CPS1_68K_PROGRAM_NO_BYTESWAP | BRF_ESS | BRF_PRG }, + { "c03js01.p6", 0x100000, 0x150e1549, CPS1_68K_PROGRAM_NO_BYTESWAP | BRF_ESS | BRF_PRG }, + { "c03js01.c01", 0x080000, 0xf92cc010, CPS1_TILES | BRF_GRA }, + { "c03js01.c03", 0x080000, 0x9abd7b1e, CPS1_TILES | BRF_GRA }, + { "c03js01.c05", 0x080000, 0x085ec243, CPS1_TILES | BRF_GRA }, + { "c03js01.c07", 0x080000, 0x2443df27, CPS1_TILES | BRF_GRA }, + { "c03.c17", 0x080000, 0x005f000b, CPS1_TILES | BRF_GRA }, + { "c03.c19", 0x080000, 0xb9441519, CPS1_TILES | BRF_GRA }, + { "c03.c21", 0x080000, 0xb7d04e8b, CPS1_TILES | BRF_GRA }, + { "c03.c23", 0x080000, 0x6b4713b4, CPS1_TILES | BRF_GRA }, + { "c03js01.c25", 0x100000, 0xe8507e5b, CPS1_TILES | BRF_GRA }, + { "c03js01.c27", 0x100000, 0x1120f48d, CPS1_TILES | BRF_GRA }, + { "c03js01.c29", 0x100000, 0xf7a36cd4, CPS1_TILES | BRF_GRA }, + { "c03js01.c31", 0x100000, 0xb0ef929a, CPS1_TILES | BRF_GRA }, + { "c03js01.m1", 0x010000, 0xa08e24b9, CPS1_Z80_PROGRAM | BRF_ESS | BRF_PRG }, + { "c03js01.v1", 0x020000, 0x897269dd, CPS1_OKIM6295_SAMPLES | BRF_SND }, + { "c03js01.v2", 0x020000, 0x3f56c8cc, CPS1_OKIM6295_SAMPLES | BRF_SND }, +}; + +STD_ROM_PICK(Striderjs01) +STD_ROM_FN(Striderjs01) + static struct BurnRomInfo UnsquadRomDesc[] = { { "aru_30.11f", 0x020000, 0x24d8f88d, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_BYTESWAP }, { "aru_35.11h", 0x020000, 0x8b954b59, BRF_ESS | BRF_PRG | CPS1_68K_PROGRAM_BYTESWAP }, @@ -17647,6 +17754,8 @@ static const struct GameConfig ConfigTable[] = { "ffightae" , CPS_B_21_DEF, mapper_S224B , 0, NULL }, { "ffightaemgc" , CPS_B_21_DEF, mapper_S224B , 0, NULL }, { "ffighta" , CPS_B_04 , mapper_S224B , 0, NULL }, + { "ffightus01" , CPS_B_04 , mapper_FFIGHTEX, 0, NULL }, + { "ffightjs01" , CPS_B_04 , mapper_FFIGHTEX, 0, NULL }, { "ffightu" , CPS_B_04 , mapper_S224B , 0, NULL }, { "ffightu1" , CPS_B_04 , mapper_S224B , 0, NULL }, { "ffightu2" , CPS_B_04 , mapper_S224B , 0, NULL }, @@ -17908,6 +18017,8 @@ static const struct GameConfig ConfigTable[] = { "slampic" , CPS_B_21_QS4, mapper_MB63B , 0, slammast_patch }, { "slampic2" , CPS_B_21_QS4, mapper_MB63B , 0, NULL }, { "strider" , CPS_B_01 , mapper_ST24M1, 1, NULL }, + { "striderus01" , CPS_B_01 , mapper_STRIDEREX, 1, NULL }, + { "striderjs01" , CPS_B_21_DEF, mapper_STRIDEREX, 1, NULL }, { "striderua" , CPS_B_01 , mapper_ST24M1, 1, NULL }, { "strideruc" , CPS_B_17 , mapper_ST24M1, 1, NULL }, { "striderjr" , CPS_B_21_DEF, mapper_ST24M1, 1, NULL }, @@ -22087,6 +22198,29 @@ static INT32 StriderjrInit() return nRet; } +// EX adds a program ROM before the graphics; derive the star ROM index +// from the loaded program inventory rather than the stock parent's index. +static INT32 StriderusexInit() +{ + INT32 nRet = Cps1RasterInit(); + if (!nRet) { + CpsStar = CpsGfx + nCpsGfxLen - 0x2000; + CpsLoadStars(CpsStar, nCps68KByteswapRomNum + nCps68KNoByteswapRomNum); + } + return nRet; +} + +static INT32 StriderjexInit() +{ + INT32 nRet = TwelveMhzInit(); + if (!nRet) { + CpsStar = CpsGfx + nCpsGfxLen - 0x2000; + CpsLoadStars(CpsStar, nCps68KByteswapRomNum + nCps68KNoByteswapRomNum); + Cps1DrawAtVblank = 1; + } + return nRet; +} + static INT32 StriderjInit() { INT32 nRet = 0; @@ -23716,6 +23850,26 @@ struct BurnDriver BurnDrvCpsDynwarjr = { &CpsRecalcPal, 0x1000, 384, 224, 4, 3 }; +struct BurnDriver BurnDrvCpsFfightus01 = { + "ffightus01", "ffight", NULL, NULL, "2026", + "Final Fight EX (USA, Backport RC4)\0", NULL, "Strygo", "CPS1", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS1, GBF_SCRFIGHT, 0, + NULL, Ffightus01RomInfo, Ffightus01RomName, NULL, NULL, NULL, NULL, FfightInputInfo, FfightDIPInfo, + DrvInit, DrvExit, Cps1Frame, CpsRedraw, CpsAreaScan, + &CpsRecalcPal, 0x1000, 384, 224, 4, 3 +}; + +struct BurnDriver BurnDrvCpsFfightjs01 = { + "ffightjs01", "ffight", NULL, NULL, "2026", + "Final Fight EX (Japan, Backport RC4)\0", NULL, "Strygo", "CPS1", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS1, GBF_SCRFIGHT, 0, + NULL, Ffightjs01RomInfo, Ffightjs01RomName, NULL, NULL, NULL, NULL, FfightInputInfo, FfightDIPInfo, + DrvInit, DrvExit, Cps1Frame, CpsRedraw, CpsAreaScan, + &CpsRecalcPal, 0x1000, 384, 224, 4, 3 +}; + struct BurnDriver BurnDrvCpsFfight = { "ffight", NULL, NULL, NULL, "1989", "Final Fight (World, set 1)\0", NULL, "Capcom", "CPS1", @@ -26336,6 +26490,26 @@ struct BurnDriver BurnDrvCpsStriderjr = { &CpsRecalcPal, 0x1000, 384, 224, 4, 3 }; +struct BurnDriver BurnDrvCpsStriderus01 = { + "striderus01", "strider", NULL, NULL, "2026", + "Strider EX (USA, Backport RC1)\0", NULL, "Strygo", "CPS1", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS1, GBF_PLATFORM | GBF_SCRFIGHT, 0, + NULL, Striderus01RomInfo, Striderus01RomName, NULL, NULL, NULL, NULL, StriderInputInfo, StrideruaDIPInfo, + StriderusexInit, DrvExit, Cps1Frame, CpsRedraw, CpsAreaScan, + &CpsRecalcPal, 0x1000, 384, 224, 4, 3 +}; + +struct BurnDriver BurnDrvCpsStriderjs01 = { + "striderjs01", "strider", NULL, NULL, "2026", + "Strider Hiryu EX (Japan, Backport RC1)\0", NULL, "Strygo", "CPS1", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS1, GBF_PLATFORM | GBF_SCRFIGHT, 0, + NULL, Striderjs01RomInfo, Striderjs01RomName, NULL, NULL, NULL, NULL, StriderInputInfo, StriderDIPInfo, + StriderjexInit, DrvExit, Cps1Frame, CpsRedraw, CpsAreaScan, + &CpsRecalcPal, 0x1000, 384, 224, 4, 3 +}; + struct BurnDriver BurnDrvCpsStriderj = { "striderj", "strider", NULL, NULL, "1989", "Strider Hiryu (Japan)\0", NULL, "Capcom", "CPS1", diff --git a/src/burn/drv/capcom/d_cps2_custom.cpp b/src/burn/drv/capcom/d_cps2_custom.cpp index 0caf68650..637f5717a 100644 --- a/src/burn/drv/capcom/d_cps2_custom.cpp +++ b/src/burn/drv/capcom/d_cps2_custom.cpp @@ -1323,35 +1323,35 @@ static INT32 VampireArrangeInit() return Cps2PlusInit(24); } -// Vampire Savior Arrange (Hack, English RC3) +// Vampire Savior Arrange (Hack, English RC4) static struct BurnRomInfo Vsavs01RomDesc[] = { - { "c71s01.p1", 0x080000, 0x33bd7728, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "c71s01.p2", 0x080000, 0x23d6fc9b, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "c71s01.p3", 0x080000, 0xf0dbcf28, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s01.p1", 0x080000, 0x1f2f188a, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s01.p2", 0x080000, 0x8926858a, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s01.p3", 0x080000, 0x2b02f280, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, { "vm3j.06b", 0x080000, 0x2c4297e0, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "c71s01.p5", 0x080000, 0x7af80c9c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s01.p5", 0x080000, 0x7854576e, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, { "vm3j.08a", 0x080000, 0x5773e5c9, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, { "c71s01.p7", 0x080000, 0x6428a025, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, { "c71s01.p8", 0x080000, 0x29316daa, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "c71s01.p9", 0x400000, 0xa7e15abc, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s01.p9", 0x400000, 0x7399ea53, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "c71s01.c1", 0x400000, 0x2eb67fcc, CPS2_GFX | BRF_GRA }, - { "c71s01.c2", 0x400000, 0xaee349f0, CPS2_GFX | BRF_GRA }, - { "c71s01.c3", 0x400000, 0x0e345bb8, CPS2_GFX | BRF_GRA }, - { "c71s01.c4", 0x400000, 0x33faf930, CPS2_GFX | BRF_GRA }, + { "c71s01.c1", 0x400000, 0x6a3fe97b, CPS2_GFX | BRF_GRA }, + { "c71s01.c2", 0x400000, 0xd64c3c03, CPS2_GFX | BRF_GRA }, + { "c71s01.c3", 0x400000, 0x93423093, CPS2_GFX | BRF_GRA }, + { "c71s01.c4", 0x400000, 0x76f89f2b, CPS2_GFX | BRF_GRA }, { "vm3.14m", 0x400000, 0xc1a28e6c, CPS2_GFX | BRF_GRA }, { "vm3.16m", 0x400000, 0x194a7304, CPS2_GFX | BRF_GRA }, { "vm3.18m", 0x400000, 0xdf9a9f47, CPS2_GFX | BRF_GRA }, { "vm3.20m", 0x400000, 0xc22fc3d9, CPS2_GFX | BRF_GRA }, - { "c71s01.c9", 0x200000, 0x8507f41f, CPS2_GFX | BRF_GRA }, - { "c71s01.c10", 0x200000, 0x68b94ec0, CPS2_GFX | BRF_GRA }, - { "c71s01.c11", 0x200000, 0x8f7e8873, CPS2_GFX | BRF_GRA }, - { "c71s01.c12", 0x200000, 0x25f15b49, CPS2_GFX | BRF_GRA }, + { "c71s01.c9", 0x200000, 0xd015d692, CPS2_GFX | BRF_GRA }, + { "c71s01.c10", 0x200000, 0x0fa94d8b, CPS2_GFX | BRF_GRA }, + { "c71s01.c11", 0x200000, 0x07f100d0, CPS2_GFX | BRF_GRA }, + { "c71s01.c12", 0x200000, 0x1ce74d45, CPS2_GFX | BRF_GRA }, { "c71s01.m1", 0x020000, 0xb866c03c, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, { "vm3.02", 0x020000, 0xcc09faa1, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - { "c71s01.m3", 0x040000, 0xc95152a3, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "c71s01.m3", 0x040000, 0xad73c4e2, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, { "vm3.11m", 0x400000, 0xe80e956e, CPS2_QSND | BRF_SND }, { "vm3.12m", 0x400000, 0x9cd71557, CPS2_QSND | BRF_SND }, @@ -1365,7 +1365,7 @@ STD_ROM_FN(Vsavs01) struct BurnDriver BurnDrvCpsVsavs01 = { "vsavs01", "vsav", NULL, NULL, "2026", - "Vampire Savior Arrange (Hack, English RC3)\0", NULL, "Strygo", "CPS2", + "Vampire Savior Arrange (Hack, English RC4)\0", NULL, "Strygo", "CPS2", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_DSTLK, NULL, Vsavs01RomInfo, Vsavs01RomName, NULL, NULL, NULL, NULL, Cps2CustomInputInfo, NULL, @@ -1373,35 +1373,35 @@ struct BurnDriver BurnDrvCpsVsavs01 = { &CpsRecalcPal, 0x1000, 384, 224, 4, 3 }; -// Vampire Savior Arrange (Hack, Japan RC3) +// Vampire Savior Arrange (Hack, Japan RC4) static struct BurnRomInfo Vsavs02RomDesc[] = { - { "c71s02.p1", 0x080000, 0x91599c43, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "c71s02.p2", 0x080000, 0x989cde13, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "c71s02.p3", 0x080000, 0xf0dbcf28, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s02.p1", 0x080000, 0x2924c56a, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s02.p2", 0x080000, 0x326ca702, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s02.p3", 0x080000, 0x2b02f280, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, { "vm3j.06b", 0x080000, 0x2c4297e0, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "c71s02.p5", 0x080000, 0x853d837e, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s02.p5", 0x080000, 0x8791d88c, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, { "vm3j.08a", 0x080000, 0x5773e5c9, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, { "c71s02.p7", 0x080000, 0x6428a025, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, { "c71s02.p8", 0x080000, 0x29316daa, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "c71s02.p9", 0x400000, 0xe2089282, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, + { "c71s02.p9", 0x400000, 0x3670226d, CPS2_PRG_68K | BRF_ESS | BRF_PRG }, - { "c71s02.c1", 0x400000, 0x2eb67fcc, CPS2_GFX | BRF_GRA }, - { "c71s02.c2", 0x400000, 0xaee349f0, CPS2_GFX | BRF_GRA }, - { "c71s02.c3", 0x400000, 0x0e345bb8, CPS2_GFX | BRF_GRA }, - { "c71s02.c4", 0x400000, 0x33faf930, CPS2_GFX | BRF_GRA }, + { "c71s02.c1", 0x400000, 0x6a3fe97b, CPS2_GFX | BRF_GRA }, + { "c71s02.c2", 0x400000, 0xd64c3c03, CPS2_GFX | BRF_GRA }, + { "c71s02.c3", 0x400000, 0x93423093, CPS2_GFX | BRF_GRA }, + { "c71s02.c4", 0x400000, 0x76f89f2b, CPS2_GFX | BRF_GRA }, { "vm3.14m", 0x400000, 0xc1a28e6c, CPS2_GFX | BRF_GRA }, { "vm3.16m", 0x400000, 0x194a7304, CPS2_GFX | BRF_GRA }, { "vm3.18m", 0x400000, 0xdf9a9f47, CPS2_GFX | BRF_GRA }, { "vm3.20m", 0x400000, 0xc22fc3d9, CPS2_GFX | BRF_GRA }, - { "c71s02.c9", 0x200000, 0x8507f41f, CPS2_GFX | BRF_GRA }, - { "c71s02.c10", 0x200000, 0x68b94ec0, CPS2_GFX | BRF_GRA }, - { "c71s02.c11", 0x200000, 0x8f7e8873, CPS2_GFX | BRF_GRA }, - { "c71s02.c12", 0x200000, 0x25f15b49, CPS2_GFX | BRF_GRA }, + { "c71s02.c9", 0x200000, 0xd015d692, CPS2_GFX | BRF_GRA }, + { "c71s02.c10", 0x200000, 0x0fa94d8b, CPS2_GFX | BRF_GRA }, + { "c71s02.c11", 0x200000, 0x07f100d0, CPS2_GFX | BRF_GRA }, + { "c71s02.c12", 0x200000, 0x1ce74d45, CPS2_GFX | BRF_GRA }, { "c71s02.m1", 0x020000, 0xb866c03c, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, { "vm3.02", 0x020000, 0xcc09faa1, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, - { "c71s02.m3", 0x040000, 0xc95152a3, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, + { "c71s02.m3", 0x040000, 0xad73c4e2, CPS2_PRG_Z80 | BRF_ESS | BRF_PRG }, { "vm3.11m", 0x400000, 0xe80e956e, CPS2_QSND | BRF_SND }, { "vm3.12m", 0x400000, 0x9cd71557, CPS2_QSND | BRF_SND }, @@ -1415,7 +1415,7 @@ STD_ROM_FN(Vsavs02) struct BurnDriver BurnDrvCpsVsavs02 = { "vsavs02", "vsav", NULL, NULL, "2026", - "Vampire Savior Arrange (Hack, Japan RC3)\0", NULL, "Strygo", "CPS2", + "Vampire Savior Arrange (Hack, Japan RC4)\0", NULL, "Strygo", "CPS2", NULL, NULL, NULL, NULL, BDF_GAME_WORKING | BDF_CLONE | BDF_HACK, 2, HARDWARE_CAPCOM_CPS2, GBF_VSFIGHT, FBF_DSTLK, NULL, Vsavs02RomInfo, Vsavs02RomName, NULL, NULL, NULL, NULL, Cps2CustomInputInfo, NULL, From 209d0a84748daf7603c7456e724d7fd502583513 Mon Sep 17 00:00:00 2001 From: dinkc64 Date: Tue, 27 Oct 2026 20:52:33 -0400 Subject: [PATCH 19/26] williams2, wip --- src/burn/drv/pre90s/d_williams.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/burn/drv/pre90s/d_williams.cpp b/src/burn/drv/pre90s/d_williams.cpp index 8050f878b..06d2790c9 100644 --- a/src/burn/drv/pre90s/d_williams.cpp +++ b/src/burn/drv/pre90s/d_williams.cpp @@ -2657,8 +2657,6 @@ static INT32 DrvFrame() { scanline = i; - CPU_RUN(0, M6809); - if (williams2) { if ((scanline & 0x1f) == 0 && scanline < 256) { // va11 pia_set_input_cb1(0, scanline & 0x20); @@ -2680,6 +2678,7 @@ static INT32 DrvFrame() if (scanline == 0 || scanline == 240) pia_set_input_ca1(1, scanline >= 240 ? 1 : 0); } + CPU_RUN(0, M6809); M6800Open(0); if (has_s11_bg) joust2_bg_cb2_flush(); // slightly delayed message(s) from s11bg -> main sound From 6b885ecd4bbab4d066dd0794c2eedde280b5ed35 Mon Sep 17 00:00:00 2001 From: dinkc64 Date: Wed, 28 Oct 2026 02:05:20 -0400 Subject: [PATCH 20/26] mutantf, fix sprite blending --- src/burn/drv/dataeast/d_cninja.cpp | 94 +++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 16 deletions(-) diff --git a/src/burn/drv/dataeast/d_cninja.cpp b/src/burn/drv/dataeast/d_cninja.cpp index bc756af02..4fc007c9e 100644 --- a/src/burn/drv/dataeast/d_cninja.cpp +++ b/src/burn/drv/dataeast/d_cninja.cpp @@ -36,6 +36,7 @@ static UINT8 *DrvSprBuf1; static UINT8 *DrvZ80RAM; static UINT32 *DrvPalette; +static UINT16 *DrvTmpDraw[3]; // mutantf alpha temp. mixing static UINT8 DrvRecalc; static UINT8 *soundlatch; @@ -1057,6 +1058,10 @@ static INT32 MemIndex() DrvPalette = (UINT32*)Next; Next += 0x0800 * sizeof(UINT32); + for (INT32 i = 0; i < 3; i++) { + DrvTmpDraw[i] = (UINT16*)Next; Next += 512 * 256 * sizeof(UINT16); + } + AllRam = Next; Drv68KRAM = Next; Next += 0x008000; @@ -1915,7 +1920,7 @@ static void cninjabl_draw_sprites() } } -static void mutantf_draw_sprites(UINT8 *ram, UINT8 *gfx, INT32 colbank, INT32 gfxbank) +static void mutantf_draw_sprites(UINT8 *ram, UINT8 *gfx, INT32 colbank, INT32 gfxbank, UINT16 *dest) { UINT16 *spriteptr = (UINT16*)ram; @@ -1937,7 +1942,6 @@ static void mutantf_draw_sprites(UINT8 *ram, UINT8 *gfx, INT32 colbank, INT32 gf while (offs != end) { INT32 x, y, sprite, colour, fx, fy, w, h, sx, sy, x_mult, y_mult; - INT32 alpha = 0xff; sprite = BURN_ENDIAN_SWAP_INT16(spriteptr[offs + 3]); if (!sprite) @@ -1962,7 +1966,6 @@ static void mutantf_draw_sprites(UINT8 *ram, UINT8 *gfx, INT32 colbank, INT32 gf if (gfxbank == 4) { - alpha = 0x80; colour &= 0xf; } @@ -2000,8 +2003,7 @@ static void mutantf_draw_sprites(UINT8 *ram, UINT8 *gfx, INT32 colbank, INT32 gf { for (y = 0; y < h; y++) { - // needs alpha blending... - deco16_draw_prio_sprite(pTransDraw, gfx, sprite + y + h * x, (colour << 4) + colbank, sx + x_mult * (w-x), sy + y_mult * (h-y), fx, fy, 0); + deco16_draw_prio_sprite(dest, gfx, sprite + y + h * x, (colour << 4) + colbank, sx + x_mult * (w-x), sy + y_mult * (h-y), fx, fy, 0); } } offs += inc; @@ -2179,6 +2181,36 @@ static INT32 Robocop2Draw() } +static inline UINT32 mutantf_alpha_blend(UINT32 d, UINT32 s, UINT32 p) +{ + INT32 a = 256 - p; + + return (((((s & 0xff00ff) * p) + ((d & 0xff00ff) * a)) & 0xff00ff00) + + ((((s & 0x00ff00) * p) + ((d & 0x00ff00) * a)) & 0x00ff0000)) / 256; +} + +// mix a 16-bit (palette index) layer onto the 32bpp output, 0 = transparent +static void mutantf_mix_layer(UINT16 *src, INT32 alpha) +{ + UINT8 *dst = pBurnDraw; + + for (INT32 y = 0; y < nScreenHeight; y++, src += nScreenWidth, dst += nBurnPitch) + { + UINT32 *d = (UINT32*)dst; + + for (INT32 x = 0; x < nScreenWidth; x++) + { + if (src[x] == 0) continue; + + if (alpha == 0xff) { + d[x] = DrvPalette[src[x]]; + } else { + d[x] = mutantf_alpha_blend(d[x], DrvPalette[src[x]], alpha); + } + } + } +} + static INT32 MutantfDraw() { // if (DrvRecalc) { @@ -2195,24 +2227,54 @@ static INT32 MutantfDraw() if (nSpriteEnable & 2) deco16_draw_layer(1, pTransDraw, 0); if (nSpriteEnable & 4) deco16_draw_layer(2, pTransDraw, 0); + if (nBurnBpp != 4) + { + // no alpha @ < 32bpp + if (deco16_priority & 1) + { + deco16_clear_prio_map(); + if (nBurnLayer & 1) mutantf_draw_sprites(DrvSprBuf, DrvGfxROM3, 0x100, 3, pTransDraw); + deco16_clear_prio_map(); + if (nBurnLayer & 2) mutantf_draw_sprites(DrvSprBuf1, DrvGfxROM4, 0x700, 4, pTransDraw); + } + else + { + deco16_clear_prio_map(); + if (nBurnLayer & 2) mutantf_draw_sprites(DrvSprBuf1, DrvGfxROM4, 0x700, 4, pTransDraw); + deco16_clear_prio_map(); + if (nBurnLayer & 1) mutantf_draw_sprites(DrvSprBuf, DrvGfxROM3, 0x100, 3, pTransDraw); + } + + if (nSpriteEnable & 8) deco16_draw_layer(0, pTransDraw, 0); + + BurnTransferCopy(DrvPalette); + + return 0; + } + + for (INT32 i = 0; i < 3; i++) memset(DrvTmpDraw[i], 0, nScreenWidth * nScreenHeight * sizeof(UINT16)); + + deco16_clear_prio_map(); + if (nBurnLayer & 1) mutantf_draw_sprites(DrvSprBuf, DrvGfxROM3, 0x100, 3, DrvTmpDraw[0]); + deco16_clear_prio_map(); + if (nBurnLayer & 2) mutantf_draw_sprites(DrvSprBuf1, DrvGfxROM4, 0x700, 4, DrvTmpDraw[1]); + + if (nSpriteEnable & 8) deco16_draw_layer(0, DrvTmpDraw[2], 0); + + BurnTransferCopy(DrvPalette); + if (deco16_priority & 1) { - deco16_clear_prio_map(); - mutantf_draw_sprites(DrvSprBuf, DrvGfxROM3, 0x100, 3); - deco16_clear_prio_map(); - mutantf_draw_sprites(DrvSprBuf1, DrvGfxROM4, 0x700, 4); + mutantf_mix_layer(DrvTmpDraw[0], 0xff); + mutantf_mix_layer(DrvTmpDraw[1], 0x80); } else { - deco16_clear_prio_map(); - mutantf_draw_sprites(DrvSprBuf1, DrvGfxROM4, 0x700, 4); - deco16_clear_prio_map(); - mutantf_draw_sprites(DrvSprBuf, DrvGfxROM3, 0x100, 3); + mutantf_mix_layer(DrvTmpDraw[1], 0x80); + mutantf_mix_layer(DrvTmpDraw[0], 0xff); } - if (nSpriteEnable & 8) deco16_draw_layer(0, pTransDraw, 0); - - BurnTransferCopy(DrvPalette); + mutantf_mix_layer(DrvTmpDraw[2], 0xff); return 0; } From 56db8a4fb403b946ef22da7999f2cdfebc79f203 Mon Sep 17 00:00:00 2001 From: SpiralBrad Date: Wed, 28 Oct 2026 16:30:39 -0500 Subject: [PATCH 21/26] d_namcos2: add valkyrienmv5 (Valkyrie no Densetsu, English translation from Namco Museum Vol. 5) (#2844) Hack clone of valkyrie with Namco's official English script from the PS1 Namco Museum Vol. 5 release (The Legend of Valkyrie) ported into the arcade program ROMs. Only wd1mpr0/wd1mpr1 differ from the parent. Source: https://github.com/SpiralBrad/valkyrie-no-densetsu-english --- src/burn/drv/pst90s/d_namcos2.cpp | 63 +++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/burn/drv/pst90s/d_namcos2.cpp b/src/burn/drv/pst90s/d_namcos2.cpp index fe48ba2a5..087c72095 100644 --- a/src/burn/drv/pst90s/d_namcos2.cpp +++ b/src/burn/drv/pst90s/d_namcos2.cpp @@ -4848,6 +4848,69 @@ struct BurnDriver BurnDrvValkyrie = { }; +// Valkyrie no Densetsu (Hack, English Translation from Namco Museum Vol. 5) +// https://github.com/SpiralBrad/valkyrie-no-densetsu-english + +static struct BurnRomInfo valkyrienmv5RomDesc[] = { + { "wd1mpr0.bin", 0x20000, 0xf6e93a8c, 0x01 | BRF_PRG | BRF_ESS }, // 0 Main 68K Code + { "wd1mpr1.bin", 0x20000, 0x0f496013, 0x01 | BRF_PRG | BRF_ESS }, // 1 + + { "wd1spr0.bin", 0x10000, 0xb2398321, 0x02 | BRF_PRG | BRF_ESS }, // 2 Sub 68K Code + { "wd1spr1.bin", 0x10000, 0x38dba897, 0x02 | BRF_PRG | BRF_ESS }, // 3 + + { "wd1snd0.bin", 0x20000, 0xd0fbf58b, 0x03 | BRF_PRG | BRF_ESS }, // 4 M6809 Code + +#if !defined ROM_VERIFY + { "sys2mcpu.bin", 0x02000, 0xa342a97e, 0x04 | BRF_PRG | BRF_ESS }, // 5 HD68705 Code +#endif + { "sys2c65c.bin", 0x08000, 0xa5b2a4ff, 0x04 | BRF_PRG | BRF_ESS }, // 6 + + { "wdobj0.bin", 0x40000, 0xe8089451, 0x05 | BRF_GRA }, // 7 Sprites + { "wdobj1.bin", 0x40000, 0x7ca65666, 0x05 | BRF_GRA }, // 8 + { "wdobj2.bin", 0x40000, 0x7c159407, 0x05 | BRF_GRA }, // 9 + { "wdobj3.bin", 0x40000, 0x649f8760, 0x05 | BRF_GRA }, // 10 + { "wdobj4.bin", 0x40000, 0x7ca39ae7, 0x05 | BRF_GRA }, // 11 + { "wdobj5.bin", 0x40000, 0x9ead2444, 0x05 | BRF_GRA }, // 12 + { "wdobj6.bin", 0x40000, 0x9fa2ea21, 0x05 | BRF_GRA }, // 13 + { "wdobj7.bin", 0x40000, 0x66e07a36, 0x05 | BRF_GRA }, // 14 + + { "wdchr0.bin", 0x20000, 0xdebb0116, 0x06 | BRF_GRA }, // 15 Layer Tiles + { "wdchr1.bin", 0x20000, 0x8a1431e8, 0x06 | BRF_GRA }, // 16 + { "wdchr2.bin", 0x20000, 0x62f75f69, 0x06 | BRF_GRA }, // 17 + { "wdchr3.bin", 0x20000, 0xcc43bbe7, 0x06 | BRF_GRA }, // 18 + { "wdchr4.bin", 0x20000, 0x2f73d05e, 0x06 | BRF_GRA }, // 19 + { "wdchr5.bin", 0x20000, 0xb632b2ec, 0x06 | BRF_GRA }, // 20 + + { "wdroz0.bin", 0x20000, 0xf776bf66, 0x07 | BRF_GRA }, // 21 Roz Layer Tiles + { "wdroz1.bin", 0x20000, 0xc1a345c3, 0x07 | BRF_GRA }, // 22 + { "wdroz2.bin", 0x20000, 0x28ffb44a, 0x07 | BRF_GRA }, // 23 + { "wdroz3.bin", 0x20000, 0x7e77b46d, 0x07 | BRF_GRA }, // 24 + + { "wdshape.bin", 0x20000, 0x3b5e0249, 0x08 | BRF_GRA }, // 25 Layer Tiles Mask Data + + { "wd1dat0.13s", 0x20000, 0xea209f48, 0x09 | BRF_PRG | BRF_ESS }, // 26 Shared 68K Data + { "wd1dat1.13p", 0x20000, 0x04b48ada, 0x09 | BRF_PRG | BRF_ESS }, // 27 + + { "wd1voi1.bin", 0x40000, 0xf1ace193, 0x0a | BRF_SND }, // 28 C140 Samples Samples + { "wd1voi2.bin", 0x20000, 0xe95c5cf3, 0x0a | BRF_SND }, // 29 + + { "valkyrie.nv", 0x02000, 0xd5ce4069, 0x0b | BRF_PRG | BRF_ESS}, // 30 nvram +}; + +STD_ROM_PICK(valkyrienmv5) +STD_ROM_FN(valkyrienmv5) + +struct BurnDriver BurnDrvValkyrienmv5 = { + "valkyrienmv5", "valkyrie", NULL, NULL, "2026", + "Valkyrie no Densetsu (Hack, English Translation from Namco Museum Vol. 5)\0", NULL, "hack (SpiralBrad)", "System 2", + NULL, NULL, NULL, NULL, + BDF_GAME_WORKING | BDF_CLONE | BDF_HACK | BDF_ORIENTATION_VERTICAL | BDF_ORIENTATION_FLIPPED | BDF_HISCORE_SUPPORTED, 2, HARDWARE_MISC_PRE90S, GBF_SCRFIGHT, 0, + NULL, valkyrienmv5RomInfo, valkyrienmv5RomName, NULL, NULL, NULL, NULL, DefaultInputInfo, DefaultDIPInfo, + ValkyrieInit, Namcos2Exit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0x4000, + 224, 288, 3, 4 +}; + + // Rolling Thunder 2 static struct BurnRomInfo rthun2RomDesc[] = { From d2bb7d347503922b50fb8850c3f2b4b5d9e100f7 Mon Sep 17 00:00:00 2001 From: barbudreadmon Date: Wed, 28 Oct 2026 23:33:57 +0100 Subject: [PATCH 22/26] improve framepacing with video sync add vrr support --- src/burner/win32/app.rc | 1 + src/burner/win32/cona.cpp | 3 ++ src/burner/win32/menu.cpp | 1 + src/burner/win32/resource.h | 1 + src/burner/win32/run.cpp | 58 ++++++++++++++++++++++++++++++ src/burner/win32/scrn.cpp | 14 ++++++++ src/intf/interface.h | 1 + src/intf/video/vid_interface.cpp | 1 + src/intf/video/win32/vid_d3d11.cpp | 44 +++++++++++++++++++++-- 9 files changed, 122 insertions(+), 2 deletions(-) diff --git a/src/burner/win32/app.rc b/src/burner/win32/app.rc index 00f86eea9..4cbea8d53 100644 --- a/src/burner/win32/app.rc +++ b/src/burner/win32/app.rc @@ -1316,6 +1316,7 @@ BEGIN MENUITEM "Enable Windowed Fullscreen (DX9/DX11 Blitters)", MENU_WINFS MENUITEM "Enable VSync (Windowed)", MENU_VIDEOVSYNC MENUITEM "Enable DWM Sync (Windowed)", MENU_DWMFIX + MENUITEM "Enable Variable Refresh Rate (DX11, Win10+)", MENU_VRR MENUITEM "Auto Frameskip", MENU_AUTOFRAMESKIP MENUITEM "Enable RunAhead", MENU_RUNAHEAD END diff --git a/src/burner/win32/cona.cpp b/src/burner/win32/cona.cpp index b492ca2f8..b70444e71 100644 --- a/src/burner/win32/cona.cpp +++ b/src/burner/win32/cona.cpp @@ -296,6 +296,7 @@ int ConfigAppLoad() VAR(bVidDX9WinFullscreen); VAR(bVidVSync); VAR(bVidDWMSync); + VAR(bVidVRR); VAR(bVidScanlines); VAR(nVidScanIntensity); @@ -687,6 +688,8 @@ int ConfigAppSave() VAR(bVidVSync); _ftprintf(h, _T("\n// If non-zero, try to synchronise to DWM on Windows 7+, this fixes frame stuttering problems.\n")); VAR(bVidDWMSync); + _ftprintf(h, _T("\n// If non-zero, use variable refresh rate (G-Sync/FreeSync) with the DX11 blitter on Windows 10+\n")); + VAR(bVidVRR); _ftprintf(h, _T("\n// Transfer method: 0 = blit from system memory / use driver/DirectX texture management;\n")); _ftprintf(h, _T("// 1 = copy to a video memory surface, then use bltfast();\n")); _ftprintf(h, _T("// -1 = autodetect for DirectDraw, equals 1 for Direct3D\n")); diff --git a/src/burner/win32/menu.cpp b/src/burner/win32/menu.cpp index 2979386ab..cb4a68ffd 100644 --- a/src/burner/win32/menu.cpp +++ b/src/burner/win32/menu.cpp @@ -1072,6 +1072,7 @@ void MenuUpdate() CheckMenuItem(hMenu, MENU_WINFS, bVidDX9WinFullscreen ? MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hMenu, MENU_DWMFIX, bVidDWMSync ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu, MENU_VRR, bVidVRR ? MF_CHECKED : MF_UNCHECKED); var = nVidSelect + MENU_BLITTER_1; CheckMenuRadioItem(hMenu, MENU_BLITTER_1, MENU_BLITTER_8, var, MF_BYCOMMAND); diff --git a/src/burner/win32/resource.h b/src/burner/win32/resource.h index dcf9ffa64..b0225f375 100644 --- a/src/burner/win32/resource.h +++ b/src/burner/win32/resource.h @@ -602,6 +602,7 @@ #define MENU_WINFS 10101 #define MENU_FULL 10102 #define MENU_DWMFIX 10103 +#define MENU_VRR 10104 #define MENU_NOSTRETCH 10110 #define MENU_STRETCH 10111 #define MENU_ASPECT 10112 diff --git a/src/burner/win32/run.cpp b/src/burner/win32/run.cpp index bd781514d..5e5beccaf 100644 --- a/src/burner/win32/run.cpp +++ b/src/burner/win32/run.cpp @@ -257,6 +257,62 @@ void do_shonky_profile() } #endif +// Present pacing when no video sync is active. +// Frames are produced when the audio engine signals a free segment, which happens on +// its own period (~10ms on Vista+), so frame times alternate e.g. 20/20/10ms. +// Presents are held back to a steady QPC schedule which follows the audio clock. +static LARGE_INTEGER qpcPaceFreq; +static LONGLONG nPaceNext = 0; + +static bool RunPaceActive() +{ + if (bVidVSync || (bVidDWMSync && !nVidFullscreen)) return false; + if (bAppDoFast || kNetGame) return false; + + return true; +} + +static void RunPacePresent() +{ + if (!RunPaceActive()) { + nPaceNext = 0; + return; + } + + if (qpcPaceFreq.QuadPart == 0) { + QueryPerformanceFrequency(&qpcPaceFreq); + } + + LONGLONG nPeriod; + if (bAudPlaying && nBurnSoundRate > 0 && nBurnSoundLen > 0) { + nPeriod = qpcPaceFreq.QuadPart * nBurnSoundLen / nBurnSoundRate; + } else { + nPeriod = qpcPaceFreq.QuadPart * 100 / nAppVirtualFps; + } + + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + + LONGLONG nLead = nPaceNext - now.QuadPart; + if (nPaceNext == 0 || nLead < 0) { + // late (or first frame): present now, the schedule follows + nPaceNext = now.QuadPart; + } else if (nLead > nPeriod) { + // schedule drifted ahead of the frame source, pull it back + nPaceNext = now.QuadPart + nPeriod; + } + + const LONGLONG nSleepMargin = qpcPaceFreq.QuadPart * 2 / 1000; + for (;;) { + QueryPerformanceCounter(&now); + LONGLONG nRemain = nPaceNext - now.QuadPart; + if (nRemain <= 0) break; + Sleep((bEnableHighResTimer && nRemain > nSleepMargin) ? 1 : 0); + } + + nPaceNext += nPeriod; +} + // With or without sound, run one frame. // If bDraw is true, it's the last frame before we are up to date, and so we should draw the screen int RunFrame(int bDraw, int bPause) @@ -267,6 +323,7 @@ int RunFrame(int bDraw, int bPause) if (bShowFPS) DisplayFPSCheckState(); if (bPrevDraw && !bPause) { + RunPacePresent(); VidPaint(0); // paint the screen (no need to validate) if (!nVidFullscreen && bVidDWMSync) // Sync to DWM (Win7+, windowed) @@ -523,6 +580,7 @@ int RunReset() { // Reset the speed throttling code nNormalLast = 0; nNormalFrac = 0; + nPaceNext = 0; // Reset FPS display DisplayFPSInit(); diff --git a/src/burner/win32/scrn.cpp b/src/burner/win32/scrn.cpp index 083f5bebe..ccee98e59 100644 --- a/src/burner/win32/scrn.cpp +++ b/src/burner/win32/scrn.cpp @@ -2295,6 +2295,8 @@ static void OnCommand(HWND /*hDlg*/, int id, HWND /*hwndCtl*/, UINT codeNotify) bVidDWMSync = !bVidDWMSync; if (bVidDWMSync && bVidVSync) bVidVSync = 0; + if (bVidDWMSync) + bVidVRR = 0; POST_INITIALISE_MESSAGE; break; @@ -2909,6 +2911,18 @@ static void OnCommand(HWND /*hDlg*/, int id, HWND /*hwndCtl*/, UINT codeNotify) bVidVSync = !bVidVSync; if (bVidVSync && bVidDWMSync) bVidDWMSync = 0; + if (bVidVSync) + bVidVRR = 0; + + POST_INITIALISE_MESSAGE; + break; + + case MENU_VRR: + bVidVRR = !bVidVRR; + if (bVidVRR) { + bVidVSync = 0; + bVidDWMSync = 0; + } POST_INITIALISE_MESSAGE; break; diff --git a/src/intf/interface.h b/src/intf/interface.h index 89af2a132..384af7096 100644 --- a/src/intf/interface.h +++ b/src/intf/interface.h @@ -241,6 +241,7 @@ extern INT64 nVidBlitterOpt[]; extern INT32 bVidFullStretch; extern INT32 bVidTripleBuffer; extern INT32 bVidVSync; +extern INT32 bVidVRR; extern double dVidCubicB; extern double dVidCubicC; extern INT32 bVidDX9Bilinear; diff --git a/src/intf/video/vid_interface.cpp b/src/intf/video/vid_interface.cpp index d6df6436f..b6209d5fe 100644 --- a/src/intf/video/vid_interface.cpp +++ b/src/intf/video/vid_interface.cpp @@ -86,6 +86,7 @@ INT32 bVidFullStretch = 0; // 1 = stretch to fill the entire window/screen INT32 bVidCorrectAspect = 1; // 1 = stretch to fill the window/screen while maintaining the correct aspect ratio INT32 bVidIntegerScale = 0; // Wants integer scaling INT32 bVidVSync = 0; // 1 = sync blits/pageflips/presents to the screen +INT32 bVidVRR = 0; // 1 = variable refresh rate (G-Sync/FreeSync), DX11 blitter only INT32 bVidTripleBuffer = 0; // 1 = use triple buffering INT32 bVidBilinear = 1; // 1 = enable bi-linear filtering (D3D blitter) INT32 bVidScanlines = 0; // 1 = draw scanlines diff --git a/src/intf/video/win32/vid_d3d11.cpp b/src/intf/video/win32/vid_d3d11.cpp index a5d025201..a22563a37 100644 --- a/src/intf/video/win32/vid_d3d11.cpp +++ b/src/intf/video/win32/vid_d3d11.cpp @@ -53,6 +53,7 @@ static const GUID FBNEO_IID_IDXGIDevice = { 0x54ec77fa, 0x1377, 0x44e6, { 0x8c, 0x32, 0x88, 0xfd, 0x5f, 0x44, 0xc8, 0x4c } }; static const GUID FBNEO_IID_IDXGIDevice1 = { 0x77db970f, 0x6276, 0x48ba, { 0xba, 0x28, 0x07, 0x01, 0x43, 0xb4, 0x39, 0x2c } }; static const GUID FBNEO_IID_IDXGIFactory = { 0x7b7166ec, 0x21c7, 0x44ae, { 0xb2, 0x1a, 0xc9, 0xae, 0x32, 0x1a, 0xe3, 0x69 } }; +static const GUID FBNEO_IID_IDXGIFactory5 = { 0x7632e1f5, 0xee65, 0x4dca, { 0x87, 0xfd, 0x84, 0xcd, 0x75, 0xf8, 0x83, 0x8d } }; static const GUID FBNEO_IID_ID3D11Texture2D = { 0x6f15aaf2, 0xd208, 0x4e89, { 0x9a, 0xb4, 0x48, 0x95, 0x35, 0xd3, 0x4f, 0x9c } }; // ID3D11ShaderReflection changed IID between d3dcompiler versions static const GUID FBNEO_IID_ID3D11ShaderReflection_47 = { 0x8d536ca1, 0x0cca, 0x4956, { 0xa8, 0x37, 0x78, 0x69, 0x63, 0x75, 0x55, 0x84 } }; @@ -61,6 +62,9 @@ static const GUID FBNEO_IID_ID3D11ShaderReflection_43 = { 0x0a233719, 0x3960, 0x // DXGI values that older headers may not have #define FBNEO_DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL ((DXGI_SWAP_EFFECT)3) #define FBNEO_DXGI_SWAP_EFFECT_FLIP_DISCARD ((DXGI_SWAP_EFFECT)4) +#define FBNEO_DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING 0x800 +#define FBNEO_DXGI_PRESENT_ALLOW_TEARING 0x200 +#define FBNEO_DXGI_FEATURE_PRESENT_ALLOW_TEARING 0 // ---------------------------------------------------------------------------- // Runtime-loaded DLLs @@ -165,6 +169,7 @@ static ID3D11RenderTargetView* pRenderTarget = NULL; static D3D_FEATURE_LEVEL nFeatureLevel = D3D_FEATURE_LEVEL_9_1; static bool bWarpDevice = false; static DXGI_SWAP_EFFECT nSwapEffect = DXGI_SWAP_EFFECT_DISCARD; +static UINT nSwapChainFlags = 0; static UINT nSwapChainWidth = 0, nSwapChainHeight = 0; static UINT nDeviceGeneration = 0; // bumped every time a device is created (OSD textures follow it) static bool bDeviceLost = false; @@ -2579,7 +2584,7 @@ static int d3d11ResizeSwapChain(UINT nWidth, UINT nHeight) pContext->OMSetRenderTargets(0, NULL, NULL); D3D11_RELEASE(pRenderTarget); - if (FAILED(pSwapChain->ResizeBuffers(0, nWidth, nHeight, DXGI_FORMAT_UNKNOWN, 0))) { + if (FAILED(pSwapChain->ResizeBuffers(0, nWidth, nHeight, DXGI_FORMAT_UNKNOWN, nSwapChainFlags))) { return 1; } @@ -2589,6 +2594,27 @@ static int d3d11ResizeSwapChain(UINT nWidth, UINT nHeight) return d3d11CreateRenderTarget(); } +// IDXGIFactory5::CheckFeatureSupport() is called through the vtable, so no DXGI 1.5 headers are needed +static bool d3d11TearingSupported(IDXGIFactory* pFactory) +{ + typedef HRESULT (STDMETHODCALLTYPE* pfnCheckFeatureSupport)(void*, UINT, void*, UINT); + void* pFactory5 = NULL; + BOOL bAllowTearing = FALSE; + + if (FAILED(pFactory->QueryInterface(FBNEO_IID_IDXGIFactory5, &pFactory5))) { + return false; + } + + pfnCheckFeatureSupport pCheckFeatureSupport = (pfnCheckFeatureSupport)((*(void***)pFactory5)[28]); + if (FAILED(pCheckFeatureSupport(pFactory5, FBNEO_DXGI_FEATURE_PRESENT_ALLOW_TEARING, &bAllowTearing, sizeof(bAllowTearing)))) { + bAllowTearing = FALSE; + } + + ((IUnknown*)pFactory5)->Release(); + + return bAllowTearing != FALSE; +} + static int d3d11CreateSwapChain() { IDXGIDevice* pDXGIDevice = NULL; @@ -2613,6 +2639,9 @@ static int d3d11CreateSwapChain() nSwapChainWidth = (rect.right > 0) ? rect.right : 1; nSwapChainHeight = (rect.bottom > 0) ? rect.bottom : 1; + bool bTearing = bVidVRR && d3d11TearingSupported(pFactory); + nSwapChainFlags = 0; + // Prefer the flip model (Win10: FLIP_DISCARD, Win8: FLIP_SEQUENTIAL), fall back to the blt model (Win7) static const DXGI_SWAP_EFFECT SwapEffects[] = { FBNEO_DXGI_SWAP_EFFECT_FLIP_DISCARD, FBNEO_DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, DXGI_SWAP_EFFECT_DISCARD }; @@ -2630,9 +2659,11 @@ static int d3d11CreateSwapChain() scd.OutputWindow = hD3D11View; scd.Windowed = TRUE; scd.SwapEffect = SwapEffects[i]; + scd.Flags = (bFlip && bTearing) ? FBNEO_DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0; if (SUCCEEDED(pFactory->CreateSwapChain(pDevice, &scd, &pSwapChain))) { nSwapEffect = SwapEffects[i]; + nSwapChainFlags = scd.Flags; } else { pSwapChain = NULL; } @@ -3447,7 +3478,10 @@ static int d3d11Paint(int bValidate) return 1; } - HRESULT hr = pSwapChain->Present((bVidVSync && !EmulatorAppIsFast()) ? 1 : 0, 0); + UINT nSyncInterval = (bVidVSync && !EmulatorAppIsFast()) ? 1 : 0; + // VRR: present immediately, the display follows our own frame pacing + UINT nPresentFlags = (nSyncInterval == 0 && (nSwapChainFlags & FBNEO_DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING)) ? FBNEO_DXGI_PRESENT_ALLOW_TEARING : 0; + HRESULT hr = pSwapChain->Present(nSyncInterval, nPresentFlags); bInPaint = false; @@ -3489,6 +3523,12 @@ static int d3d11GetSettings(InterfaceInfo* pInfo) IntInfoAddStringModule(pInfo, _T("Using a flip-model swap chain")); } + if (nSwapChainFlags & FBNEO_DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) { + IntInfoAddStringModule(pInfo, _T("Variable refresh rate (tearing) presents enabled")); + } else if (bVidVRR) { + IntInfoAddStringModule(pInfo, _T("Variable refresh rate requested, but not supported")); + } + if (nVidFullscreen) { if (bChangedDisplayMode) { _sntprintf(szString, MAX_PATH, _T("Fullscreen (%i x %i, display mode changed)"), nVidScrnWidth, nVidScrnHeight); From 621233c80cc8372573d0f5c8e321a150ece7890f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Kasperavi=C4=8Dius?= Date: Thu, 29 Oct 2026 01:21:11 +0100 Subject: [PATCH 23/26] Add an optional headless embedding layer (C API) for native frontends (#2841) * Add headless frontend for embedding FBNeo A new "headless" port that builds libfbneo as a shared library (fbneo_headless.dylib) exposing a small C API, for embedding in native host frontends. Built originally to support OpenEmu-Silicon. * Add input mapping and timing/aspect/reset to the C API Rounds out the headless frontend with the functions a host wrapper needs to actually run and control a game, instead of only loading one. Input mapping (headless_input.cpp): FBHeadlessSetInput(player, button, value) turns a simple arcade control (Up/Down/Left/Right, buttons 1-6, Start, Coin, Service) into the right input for whatever driver is loaded. It does this by reading each input's canonical name from the driver ("p1 up", "p1 fire 1", "p1 coin", "p1 start", and the "coin N"/"start N" variants) and rebinding that input to a private switch code the headless input plugin reads back. Inputs that don't match a control are parked on a dead code so they always read as released. Timing and geometry, needed for correct video/audio sync and aspect ratio: - FBHeadlessGetFrameRate() - the game's refresh rate (fps x 100) - FBHeadlessGetAspectX/Y() - the game's pixel aspect ratio - FBHeadlessReset() - reset the running game The FBHeadlessButton enum lists the supported controls in a fixed order so the host can pass them straight through. * Add save/load functions * Add support for cheats and RetroAchievements * Address comments from adversarial review * Honor BDF_16BIT_ONLY (Cave, CPS3, Megadrive) These drivers only blit RGB565. Rendering them at 32bpp produced garbled graphics. Render at 16bpp and convert to 32bpp on present (combined with the existing rotation pass so vertical games get both), fixing Cave, CPS3 and Megadrive-arcade video while keeping the 32bpp zero-copy fast path. * Map the Service and Configure keys to the test and service switches Most drivers name their cabinet switches "diag" (test switch, opens the settings menu) and "service" with no player prefix, so the headless mapper never bound them and the frontend's Service key did nothing. Service now drives the test switch, or "service" when a driver has no test switch. A new FBHeadlessButtonConfigure drives "service" in drivers that have both (it usually adds a service credit). * Map twin-stick games Drivers name the second stick inconsistently: "pN 2", or another player's plain d-pad (Robotron's "Fire Up" is "p2 up", Smash TV's fire stick is p3/p4, Angel Kids' left stick is p3). The headless mapper dropped the first form and sent the second to the wrong player, so Robotron's fire stick only worked from a second controller. When the display name names a stick (Left Stick, Right Stick, Fire, Shoot, Aim...), it now decides the stick and the player; otherwise "pN 2" is player N's right stick. Like the MAME core, the left stick goes on the d-pad and the right stick on buttons 1-4 (left, up, down, right), since OpenEmu's arcade controller has one stick. * Report little-endian cheat memory for TMS34010 and V60 boards GetMainMemoryBigEndian returned 1 for CPUs with MB_CHEAT_ENDI_SWAP, but those (TMS34010, V60) are little-endian; the flag only exists because FBNeo's own cheat engine stores values big-endian. The host then wrote multi-byte cheats in the wrong order: in Mortal Kombat "0000A260:00A1" stored bytes 00 A1, so P1 health read as 0xA100 (bar overfilled, dies in one hit). Every board now reports little-endian, which is what the 68K boards already did by the RetroAchievements/libretro convention. Headless check (mk, round 1, cheat 0000A260:00A1 at frame 3100): before: A260=00 A261=A1 after the cheat (was A1 00) after: A260=A1 A261=00, unchanged from what the game wrote * Save battery-backed RAM in the headless burner Drivers keep battery-backed RAM (ACB_NVRAM: Mortal Kombat's settings, Robotron's high scores...) only as a scan area; the frontend has to save it. The Windows burner does that with StatedAuto in DrvInit/DrvExit, but the headless burner never did, so those games lost their settings every session (unlike EEPROM games, which the device saves itself). Like the Windows burner, DrvInit(bRestore) now loads .fs and DrvExit saves it. Loading a game and Reset both restore it. Drivers without NVRAM write no file. Headless check (mk, three runs sharing the support folder, 600 frames): before: only mk.ini saved after: mk.fs saved; run 1 (blank NVRAM) sits on one screen, runs 2 and 3 boot normally and are identical. sfa still saves sfa.nv only. --- .gitignore | 1 + makefile | 3 + makefile.headless | 639 +++++++++++++++++++++++ src/burn/burn.cpp | 2 +- src/burn/cheat.cpp | 57 ++ src/burn/cheat.h | 7 + src/burner/burner.h | 2 + src/burner/headless/burner_headless.h | 140 +++++ src/burner/headless/fbheadless.h | 101 ++++ src/burner/headless/headless_drv.cpp | 134 +++++ src/burner/headless/headless_input.cpp | 209 ++++++++ src/burner/headless/headless_main.cpp | 192 +++++++ src/burner/headless/headless_memory.cpp | 217 ++++++++ src/burner/headless/headless_misc.cpp | 49 ++ src/burner/headless/tchar.h | 72 +++ src/intf/audio/aud_interface.cpp | 4 + src/intf/audio/headless/aud_headless.cpp | 49 ++ src/intf/input/headless/inp_headless.cpp | 63 +++ src/intf/input/inp_interface.cpp | 4 + src/intf/video/headless/vid_headless.cpp | 192 +++++++ src/intf/video/vid_interface.cpp | 4 + 21 files changed, 2140 insertions(+), 1 deletion(-) create mode 100644 makefile.headless create mode 100644 src/burner/headless/burner_headless.h create mode 100644 src/burner/headless/fbheadless.h create mode 100644 src/burner/headless/headless_drv.cpp create mode 100644 src/burner/headless/headless_input.cpp create mode 100644 src/burner/headless/headless_main.cpp create mode 100644 src/burner/headless/headless_memory.cpp create mode 100644 src/burner/headless/headless_misc.cpp create mode 100644 src/burner/headless/tchar.h create mode 100644 src/intf/audio/headless/aud_headless.cpp create mode 100644 src/intf/input/headless/inp_headless.cpp create mode 100644 src/intf/video/headless/vid_headless.cpp diff --git a/.gitignore b/.gitignore index ab82dd175..25fa1cc46 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ nvram *.nvr config/* fbneo.dat +fbneo_headless.dylib diff --git a/makefile b/makefile index ec5e0417b..fcefc86c2 100644 --- a/makefile +++ b/makefile @@ -120,6 +120,9 @@ sdl: FORCE sdl2: FORCE @$(MAKE) -s -f makefile.sdl2 +headless: FORCE + @$(MAKE) -s -f makefile.headless + vc: FORCE @$(MAKE) -s -f makefile.vc diff --git a/makefile.headless b/makefile.headless new file mode 100644 index 000000000..ced2ab643 --- /dev/null +++ b/makefile.headless @@ -0,0 +1,639 @@ +# Makefile for FBNeo, headless core (derived from makefile.sdl2) +# +# The first pass makes sure all intermediary targets are present. The second pass updates +# any targets, if necessary. (Intermediary) targets which have their own unique rules +# are generated as required. + +unexport + +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Darwin) +DARWIN=1 +endif + +ifdef DARWIN +PLATFLAGS += -arch arm64 -mmacosx-version-min=11.0 -Wno-deprecated-declarations +endif + + +ifeq ($(OS),Windows_NT) +WINDOWS=1 +endif + +# +# Flags. Uncomment any of these declarations to enable their function. +# + +# Check for changes in header files +ifndef SKIPDEPEND +DEPEND = 1 +endif + +# SANITIZE = 1 + +# +# Declare variables +# + +# Specify the name of the executable file, without ".exe" +NAME = fbneo + +PERL = 1 +LSB_FIRST = 1 +INCLUDE_7Z_SUPPORT = 1 + +BUILD_X86_ASM= +INCLUDE_AVI_RECORDING= +BUILD_A68K= +UNICODE= + + +# +# Specify paths/files +# + +objdir = obj/ +srcdir = src/ + +include makefile.burn_rules + +# Platform stuff +alldir += burner burner/headless burner/sdl dep/libs/libspng dep/libs/lib7z dep/libs/zlib intf intf/video \ + intf/video/scalers intf/video/headless intf/audio intf/audio/headless intf/input intf/input/headless intf/cd \ + dep/generated + +depobj += cdlist.o \ + \ + conc.o cong.o dat.o gamc.o gami.o image.o ioapi.o misc.o sshot.o state.o statec.o unzip.o zipfn.o \ + \ + adler32.o compress.o crc32.o deflate.o gzclose.o gzlib.o gzread.o gzwrite.o infback.o inffast.o inflate.o inftrees.o \ + trees.o uncompr.o zutil.o \ + \ + spng.o \ + \ + aud_dsp.o aud_interface.o cd_interface.o cd_chd.o inp_interface.o interface.o lowpass2.o vid_interface.o \ + vid_support.o \ + \ + inp_headless.o aud_headless.o vid_headless.o support_paths.o ips_manager.o scrn.o localise_gamelist.o romdata.o \ + cd_img.o stringset.o bzip.o media.o \ + inpdipsw.o inputbuf.o replay.o stated.o headless_main.o headless_drv.o headless_misc.o headless_input.o headless_memory.o + +ifdef FORCE_PULSE_AUDIO +alldir += intf/audio/linux + +depobj += aud_pulse_simple.o +endif + +ifdef INCLUDE_7Z_SUPPORT +depobj += un7z.o \ + \ + 7zArcIn.o 7zBuf.o 7zBuf2.o 7zCrc.o 7zCrcOpt.o 7zDec.o 7zFile.o 7zStream.o Alloc.o Bcj2.o Bra.o Bra86.o BraIA64.o CpuArch.o \ + Delta.o LzFindOpt.o LzmaDec.o Lzma2Dec.o MtDec.o Ppmd7.o Ppmd7Dec.o Ppmd7aDec.o Sha256.o Sha1Opt.o Sha256Opt.o Threads.o Xxh64.o Xz.o XzCrc64.o XzCrc64Opt.o XzDec.o ZstdDec.o +endif + +autobj += $(depobj) + +ifdef BUILD_X86_ASM +autobj += eagle_fm.o 2xsaimmx.o hq2x32.o hq3x32.o hq4x32.o superscale.o +endif + +# End, platform stuff + +incdir = $(foreach dir,$(alldir),-I$(srcdir)$(dir)) -I$(objdir)dep/generated + +lib = -lstdc++ -lm -lpthread + +ifdef FORCE_PULSE_AUDIO +lib += -lpulse-simple +endif + + +ifdef INCLUDE_SWITCHRES +incdir += `pkg-config --cflags switchres` +lib += `pkg-config --libs switchres` +endif + +autdep = $(depobj:.o=.d) +drvdep = $(drvsrc:.o=.d) + +ifdef BUILD_A68K +a68k.o = $(objdir)cpu/a68k/a68k.o +endif + +driverlist.h = $(srcdir)dep/generated/driverlist.h +ctv.h = $(srcdir)dep/generated/ctv.h +toa_gp9001_func.h = $(srcdir)dep/generated/toa_gp9001_func.h +neo_sprite_func.h = $(srcdir)dep/generated/neo_sprite_func.h +cave_tile_func.h = $(srcdir)dep/generated/cave_tile_func.h +cave_sprite_func.h = $(srcdir)dep/generated/cave_sprite_func.h +psikyo_tile_func.h = $(srcdir)dep/generated/psikyo_tile_func.h +pgm_sprite.h = $(srcdir)dep/generated/pgm_sprite.h +build_details.h = $(srcdir)dep/generated/build_details.h + +allobj = $(objdir)cpu/m68k/m68kcpu.o $(objdir)cpu/m68k/m68kops.o \ + $(foreach file,$(autobj:.o=.c), \ + $(foreach dir,$(alldir),$(subst $(srcdir),$(objdir), \ + $(firstword $(subst .c,.o,$(wildcard $(srcdir)$(dir)/$(file))))))) \ + $(foreach file,$(autobj:.o=.cpp), \ + $(foreach dir,$(alldir),$(subst $(srcdir),$(objdir), \ + $(firstword $(subst .cpp,.o,$(wildcard $(srcdir)$(dir)/$(file))))))) \ + $(foreach file,$(autobj:.o=.asm), \ + $(foreach dir,$(alldir),$(subst $(srcdir),$(objdir), \ + $(firstword $(subst .asm,.o,$(wildcard $(srcdir)$(dir)/$(file))))))) \ + $(foreach file,$(autobj:.o=.rc), \ + $(foreach dir,$(alldir),$(subst $(srcdir),$(objdir), \ + $(firstword $(subst .rc,.o,$(wildcard $(srcdir)$(dir)/$(file))))))) + +ifdef BUILD_A68K +allobj += $(a68k.o) +endif + +alldep = $(foreach file,$(autobj:.o=.c), \ + $(foreach dir,$(alldir),$(subst $(srcdir),$(objdir), \ + $(firstword $(subst .c,.d,$(wildcard $(srcdir)$(dir)/$(file))))))) \ + $(foreach file,$(autobj:.o=.cpp), \ + $(foreach dir,$(alldir),$(subst $(srcdir),$(objdir), \ + $(firstword $(subst .cpp,.d,$(wildcard $(srcdir)$(dir)/$(file))))))) \ + $(foreach file,$(autobj:.o=.rc), \ + $(foreach dir,$(alldir),$(subst $(srcdir),$(objdir), \ + $(firstword $(subst .rc,.d,$(wildcard $(srcdir)$(dir)/$(file))))))) + +autdrv := $(drvsrc:.cpp=.o) + +# +# +# Specify compiler/linker/assembler +# +# + +ifdef DARWIN + CC = gcc +else + CC = gcc +endif + +CXX = $(CC) +LD = $(CC) +AS = nasm + +# While cross-compiling, tools need to be compiled for host so that they can be executed by host. +# if HOST_CC is not set, then we aren't cross-compiling, so we'll use target compiler as host compiler +ifeq ($(HOST_CC),) +HOST_CC := $(CC) +HOST_CXX := $(CXX) +HOST_CFLAGS := $(CFLAGS) +HOST_CXXFLAGS := $(CXXFLAGS) +HOST_LDFLAGS := $(LDFLAGS) +endif + +#LDFLAGS = -static + +CFLAGS = -O2 -fomit-frame-pointer -Wno-write-strings \ + -Wall -Wno-long-long -Wno-sign-compare -Wno-uninitialized -Wno-unused \ + -Wno-conversion -Wno-attributes \ + -Wno-unused-parameter -Wno-unused-value -std=c99 \ + $(PLATFLAGS) $(DEF) $(incdir) + +CXXFLAGS = -O2 -fomit-frame-pointer -Wno-write-strings \ + -Wall -W -Wno-long-long -Wno-sign-compare \ + -Wunknown-pragmas -Wundef -Wconversion -Wno-missing-braces \ + -Wuninitialized -Wpointer-arith -Winline -Wno-multichar \ + -Wno-conversion -Wno-attributes \ + -Wno-unused-parameter -Wno-unused-value -Wno-narrowing \ + $(PLATFLAGS) $(DEF) $(incdir) + +ASFLAGS = -O1 -f coff -w-orphan-labels + +# D3DUtils & D3DMath need these +# DEF = -Dsinf=\(float\)sin -Dcosf=\(float\)cos -Dasinf=\(float\)asin -Dacosf=\(float\)acos -Dsqrtf=\(float\)sqrt + +# FIXME + DEF := -DBUILD_HEADLESS -DINCLUDE_FLACMP3_SUPPORT -DUSE_SPEEDHACKS -DFILENAME=$(NAME) -DUSE_FILE32API -DHAVE_UNISTD_H + +ifdef FORCE_PULSE_AUDIO + DEF := $(DEF) -DFORCE_PULSE_AUDIO +endif + +ifdef WINDOWS + DEF := $(DEF) -DSDL_WINDOWS +endif + +ifdef UNICODE + DEF := $(DEF) -D_UNICODE +endif + +ifdef SPECIALBUILD + DEF := $(DEF) -DSPECIALBUILD=$(SPECIALBUILD) +endif + +ifdef FASTCALL + DEF := $(DEF) -DFASTCALL +endif + +ifdef DEBUG + DEF := $(DEF) -DFBNEO_DEBUG +endif + +ifdef ROM_VERIFY + DEF := $(DEF) -DROM_VERIFY +endif + +ifdef INCLUDE_7Z_SUPPORT + DEF := $(DEF) -DINCLUDE_7Z_SUPPORT +endif + +ifdef INCLUDE_AVI_RECORDING + DEF := $(DEF) -DINCLUDE_AVI_RECORDING +endif + +ifdef LSB_FIRST + DEF := $(DEF) -DLSB_FIRST +endif + +ifdef INCLUDE_LIB_PNGH + DEF := $(DEF) -DINCLUDE_LIB_PNGH +endif + +ifdef BUILD_A68K + DEF := $(DEF) -DBUILD_A68K +endif + +ifdef BUILD_X86_ASM + DEF := $(DEF) -DBUILD_X86_ASM +endif + +ifdef BUILD_X64_EXE + DEF := $(DEF) -DBUILD_X64_EXE +endif + +ifdef INCLUDE_SWITCHRES + DEF := $(DEF) -DINCLUDE_SWITCHRES +endif + +ifdef SANITIZE +ifndef SYMBOL +SYMBOL =1 +endif +CFLAGS += -fsanitize=address -fsanitize=undefined -fsanitize=bounds-strict +CXXFLAGS += -fsanitize=address -fsanitize=undefined -fsanitize=bounds-strict +endif + +ifdef SYMBOL + CFLAGS += -ggdb3 -fno-omit-frame-pointer + CXXFLAGS += -ggdb3 -fno-omit-frame-pointer + ASFLAGS += -g + DEF := $(DEF) -D_DEBUG +else + LDFLAGS += -s +endif + +ifdef PROFILE + CFLAGS += -pg + CXXFLAGS += -pg +endif + + +ifdef BUILD_NATIVE + CFLAGS += -march=native -mtune=native + CXXFLAGS += -march=native -mtune=native +endif + +# For zlib +DEF := $(DEF) -DNO_VIZ -D_LARGEFILE64_SOURCE=0 -D_FILE_OFFSET_BITS=64 + +# For libchdr +DEF := $(DEF) -DCHDR_SYSTEM_ZLIB + +# For lib7z +ifdef INCLUDE_7Z_SUPPORT +DEF := $(DEF) -D_7ZIP_PPMD_SUPPPORT -D_7ZIP_ST +endif + +# +# +# Specify paths +# +# + +vpath %.asm $(foreach dir,$(alldir),$(srcdir)$(dir)/ ) +vpath %.cpp $(foreach dir,$(alldir),$(srcdir)$(dir)/ ) +vpath %.c $(foreach dir,$(alldir),$(srcdir)$(dir)/ ) +vpath %.h $(foreach dir,$(alldir),$(srcdir)$(dir)/ ) +vpath %.rc $(foreach dir,$(alldir),$(srcdir)$(dir)/ ) + +vpath %.o $(foreach dir,$(alldir),$(objdir)$(dir)/ ) +vpath %.d $(foreach dir,$(alldir),$(objdir)$(dir)/ ) + +# +# +# Rules +# +# + +.PHONY: all init cleandep touch clean + +ifeq ($(MAKELEVEL),1) +ifdef DEPEND + +all: init $(drvdep) $(autdep) $(autobj) $(autdrv) + @$(MAKE) -f makefile.headless -s + +else + +all: init $(autobj) $(autdrv) + @$(MAKE) -f makefile.headless -s + +endif +else + +all: fbneo_headless.dylib + +endif + +# +# +# Rule for linking the executable +# +# + +ifeq ($(MAKELEVEL),2) + +$(objdir)drivers.o: $(autdrv) + @echo Linking drivers... + @$(LD) -r -nostdlib -o $@ $^ + +ifdef WINDOWS + +$(NAME): $(allobj) $(objdir)drivers.o + @echo + @echo Linking executable... $(NAME) + @$(LD) -Wl,--image-base -Wl,0x10000000 -mconsole $(CFLAGS) $(LDFLAGS) -o $@ $^ $(lib) + +else + +fbneo_headless.dylib: $(allobj) $(objdir)drivers.o + @echo + @echo Linking dylib... $@ + @$(LD) -dynamiclib -install_name @rpath/fbneo_headless.dylib $(CFLAGS) $(LDFLAGS) -o $@ $^ $(lib) +endif + + +ifdef DEBUG + +# Don't compress when making a debug build + +else +ifdef COMPRESS + @upx --best $@ +endif +endif +endif + +ifeq ($(MAKELEVEL),1) +ifdef FORCE_UPDATE +$(build_details.h): FORCE +endif +endif + +# +# Generate the gamelist +# + +burn.o burn.d: $(driverlist.h) + +$(driverlist.h): $(drvsrc) $(srcdir)dep/scripts/gamelist.pl +ifdef PERL + @$(srcdir)dep/scripts/gamelist.pl -o $@ -l gamelist.txt \ + $(filter %.cpp,$(foreach file,$(drvsrc:.o=.cpp),$(foreach dir,$(alldir), \ + $(firstword $(wildcard $(srcdir)$(dir)/$(file)))))) +else +ifeq ($(MAKELEVEL),2) + @echo + @echo Warning: Perl is not available on this system. + @echo $@ cannot be updated or created! + @echo +endif +endif + +# +# Verify if driverlist.h needs to be updated +# + +#ifeq ($(MAKELEVEL),1) +#ifdef FORCE_UPDATE +#$(driverlist.h): FORCE +#endif +#endif + + +# +# Compile 68000 cores +# + +# A68K + +ifdef BUILD_A68K +$(a68k.o): fba_make68k.c + @echo Compiling A68K MC68000 core... + @$(CC) -mconsole $(CFLAGS) $(LDFLAGS) -DWIN32 -Wno-unused -Wno-conversion -Wno-missing-prototypes \ + -s $< -o $(subst $(srcdir),$(objdir),$($@ + +# +# Extra rules for generated header file toa_gp9001_func.h, needed by toa_gp9001.cpp +# + +toa_bcu2.d toa_bcu2.o toa_gp9001.d toa_gp9001.o: $(toa_gp9001_func.h) + +$(toa_gp9001_func.h): $(srcdir)dep/scripts/toa_gp9001_func.pl + @$(srcdir)dep/scripts/toa_gp9001_func.pl -o $(toa_gp9001_func.h) + +# +# Extra rules for generated header file neo_sprite_func.h, needed by neo_sprite.cpp +# + +neo_sprite.d neo_sprite.o: $(neo_sprite_func.h) + +$(neo_sprite_func.h): $(srcdir)dep/scripts/neo_sprite_func.pl + @$(srcdir)dep/scripts/neo_sprite_func.pl -o $(neo_sprite_func.h) + +# +# Extra rules for generated header file cave_tile_func.h, needed by cave_tile.cpp +# + +cave_tile.d cave_tile.o: $(cave_tile_func.h) + +$(cave_tile_func.h): $(srcdir)dep/scripts/cave_tile_func.pl + @$(srcdir)dep/scripts/cave_tile_func.pl -o $(cave_tile_func.h) + +# +# Extra rules for generated header file cave_sprite_func.h, needed by cave_sprite.cpp +# + +cave_sprite.d cave_sprite.o: $(cave_sprite_func.h) + +$(cave_sprite_func.h): $(srcdir)dep/scripts/cave_sprite_func.pl + @$(srcdir)dep/scripts/cave_sprite_func.pl -o $(cave_sprite_func.h) + +# +# Extra rules for generated header file psikyo_tile_func.h / psikyo_sprite_func.h, needed by psikyo_tile.cpp / psikyo_sprite.cpp +# + +psikyo_tile.d psikyo_tile.o psikyosprite.d psikyo_sprite.o: $(psikyo_tile_func.h) + +$(psikyo_tile_func.h): $(srcdir)dep/scripts/psikyo_tile_func.pl + $(srcdir)dep/scripts/psikyo_tile_func.pl -o $(psikyo_tile_func.h) + +# +# Extra rules for generated header file pgm_sprite.h, needed by pgm_draw.cpp +# + +pgm_draw.d pgm_draw.o: $(pgm_sprite.h) + +$(pgm_sprite.h): pgm_sprite_create.cpp + @echo Generating $(srcdir)dep/generated/$(@F)... + @$(HOST_CC) $(HOST_CXXFLAGS) $(HOST_LDFLAGS) $< \ + -o $(subst $(srcdir),$(objdir),$($@ + + +ifeq ($(MAKELEVEL),2) +ifdef DEPEND + +include $(alldep) + +endif +endif + + +# +# Generic rules for C/C++ files +# +# Note: require init to complete before assembling anything (see "| init" below) +# to avoid parallization issues on fresh builds. +# + +ifeq ($(MAKELEVEL),1) + +%.o: %.cpp + @echo Compiling $<... + @$(CC) $(CXXFLAGS) -c $< -o $(subst $(srcdir),$(objdir),$($(subst $(srcdir),$(objdir),$($(subst $(srcdir),$(objdir),$($(subst $(srcdir),$(objdir),$(nMemorySize; + return (size == ~0U) ? 0 : size; +} + +// Bulk-read a contiguous range from the main CPU (cpu 0) address space, opening +// the cpu once. Returns the number of bytes actually read (clamped to memory size). +unsigned int ReadMemoryRange(HWAddressType address, unsigned char *buffer, unsigned int length) +{ + if (!bDrvOkay || buffer == NULL) + return 0; + + cheat_ptr = &cpus[0]; + cpu_core_config *cfg = cheat_ptr->cpuconfig; + unsigned int memSize = (unsigned int)cfg->nMemorySize; + + INT32 nActiveCPU = cfg->active(); + if (nActiveCPU >= 0) cfg->close(); + cfg->open(cheat_ptr->nCPU); + + unsigned int i; + for (i = 0; i < length; i++) { + if (address + i >= memSize) break; + buffer[i] = cfg->read(address + i); + } + + cfg->close(); + if (nActiveCPU >= 0) cfg->open(nActiveCPU); + + return i; +} + +// Multi-byte order hint for the host's cheat search and cheat writes. This is +// deliberately NOT derived from nAddressFlags: RetroAchievements sets and +// libretro .cht cheats (the formats the host matches) are authored +// little-endian for the 68K arcade boards -- every FBNeo libretro .cht is +// big_endian="false" -- and the CPUs that set MB_CHEAT_ENDI_SWAP (TMS34010, +// V60) are natively little-endian; the flag only exists because FBNeo's own +// cheat engine stores values big-endian. Reporting 1 for those made the host +// write "0000A260:00A1" in Mortal Kombat as bytes 00 A1, so the game read +// P1 health as 0xA100. Every board is therefore reported little-endian. +int GetMainMemoryBigEndian() +{ + return 0; +} +#endif diff --git a/src/burn/cheat.h b/src/burn/cheat.h index ccce6c2fa..03c911c07 100644 --- a/src/burn/cheat.h +++ b/src/burn/cheat.h @@ -81,3 +81,10 @@ bool WriteValueAtHardwareAddress(HWAddressType address, unsigned int value, unsi bool WriteValueAtHardwareAddress_audio(HWAddressType address, unsigned int value, unsigned int size, int isLittleEndian); bool IsHardwareAddressValid(HWAddressType address); + +#ifdef BUILD_HEADLESS +// OpenEmu-Silicon headless helpers (see cheat.cpp). +unsigned int GetMainMemorySize(); +unsigned int ReadMemoryRange(HWAddressType address, unsigned char *buffer, unsigned int length); +int GetMainMemoryBigEndian(); // multibyte order for host cheats; always 0 (little-endian), see cheat.cpp +#endif diff --git a/src/burner/burner.h b/src/burner/burner.h index b57bdbee2..a7adff052 100644 --- a/src/burner/burner.h +++ b/src/burner/burner.h @@ -45,6 +45,8 @@ typedef struct tagIMAGE { #include "burner_macos.h" #elif defined (BUILD_SDL) || defined (BUILD_SDL2) #include "burner_sdl.h" +#elif defined (BUILD_HEADLESS) + #include "burner_headless.h" #elif defined (_XBOX) && !defined(__LIBRETRO__) #include "burner_xbox.h" #elif defined(__LIBRETRO__) diff --git a/src/burner/headless/burner_headless.h b/src/burner/headless/burner_headless.h new file mode 100644 index 000000000..cddf5cdae --- /dev/null +++ b/src/burner/headless/burner_headless.h @@ -0,0 +1,140 @@ +// Platform burner header for the headless FinalBurn Neo frontend. +// Author: Leonardo Kasperavičius +// +// Built originally to support OpenEmu-Silicon +// (https://github.com/OpenEmu-Silicon/OpenEmu-Silicon). +// +// Declares the types, globals and frontend prototypes the shared burner code +// expects (the headless counterpart of burner_sdl.h / burner_macos.h), without +// any SDL or GUI dependencies. Subject to the FB Neo license (see src/license.txt). + +#ifndef BURNER_HEADLESS_PLATFORM_H +#define BURNER_HEADLESS_PLATFORM_H + +// Win32 type aliases the shared code relies on. +#ifndef _WIN32 +typedef struct tagRECT +{ + int left; + int top; + int right; + int bottom; +} RECT, * PRECT, * LPRECT; +typedef const RECT* LPCRECT; +typedef UINT32 DWORD; +typedef UINT8 BYTE; +#else +#include +#endif + +#ifndef MAX_PATH +#define MAX_PATH 511 +#endif + +#ifndef __cdecl +#define __cdecl +#endif + +// misc.cpp +int SetBurnHighCol(int nDepth); + +extern int nAppVirtualFps; +extern bool bRunPause; +extern bool bAppDoFast; +extern char fpsstring[20]; +extern bool bAppShowFPS; +extern bool bAlwaysProcessKeyboardInput; +extern TCHAR szAppBurnVer[16]; +extern bool bAppFullscreen; +extern bool bIntegerScale; +extern int nWindowScale; +extern bool bAlwaysMenu; +extern int nGameSelect; +extern int nFilterSelect; +extern bool bShowAvailableOnly; +extern bool bShowClones; +extern int gameSelectedFromFilter; +extern TCHAR* GetIsoPath(); + +TCHAR* ANSIToTCHAR(const char* pszInString, TCHAR* pszOutString, int nOutSize); +char* TCHARToANSI(const TCHAR* pszInString, char* pszOutString, int nOutSize); + +#define _TtoA(a) TCHARToANSI(a, NULL, 0) +#define _AtoT(a) ANSIToTCHAR(a, NULL, 0) + +bool AppProcessKeyboardInput(); + +// config +int ConfigAppLoad(); +int ConfigAppSave(); + +// drv.cpp (headless) +extern int bDrvOkay; +extern char szAppRomPaths[DIRS_MAX][MAX_PATH]; +int DrvInit(int nDrvNum, bool bRestore); +int DrvInitCallback(); +int DrvExit(); +int ProgressUpdateBurner(double dProgress, const TCHAR* pszText, bool bAbs); +int AppError(TCHAR* szText, int bWarning); + +// run +extern int RunMessageLoop(); +extern int RunReset(); + +#define MESSAGE_MAX_FRAMES 180 +#define MESSAGE_MAX_LENGTH 255 + +extern UINT32 messageFrames; +extern char lastMessage[MESSAGE_MAX_LENGTH]; + +void UpdateMessage(char* message); +int StatedAuto(int bSave); + +// media.cpp +int MediaInit(); +int MediaExit(); + +// inpdipsw.cpp +#define DIP_MAX_NAME 64 +#define MAXDIPSWITCHES 32 +#define MAXDIPOPTIONS 32 +bool setDIPSwitchOption(int dipgroup, int dipoption); +int InpDIPSWCreate(); +void InpDIPSWResetDIPs(); + +struct GroupOfDIPSwitches +{ + BurnDIPInfo dipSwitch; + UINT16 DefaultDIPOption; + UINT16 SelectedDIPOption; + char OptionsNamesWithCheckBoxes[MAXDIPOPTIONS][DIP_MAX_NAME]; + BurnDIPInfo dipSwitchesOptions[MAXDIPOPTIONS]; +}; + +// inp_interface.cpp +int InputInit(); +int InputExit(); +int InputMake(bool bCopy); + +// stated.cpp +int QuickState(int bSave); +extern int bDrvSaveAll; + +// stringset.cpp +class StringSet { +public: + TCHAR* szText; + int nLen; + int __cdecl Add(TCHAR* szFormat, ...); + int Reset(); + + StringSet(); + ~StringSet(); +}; + +// support_paths.cpp +extern TCHAR szAppListsPath[MAX_PATH]; +extern TCHAR szAppDatListsPath[MAX_PATH]; +extern TCHAR szAppArchivesPath[MAX_PATH]; + +#endif // BURNER_HEADLESS_PLATFORM_H diff --git a/src/burner/headless/fbheadless.h b/src/burner/headless/fbheadless.h new file mode 100644 index 000000000..b44b4edc4 --- /dev/null +++ b/src/burner/headless/fbheadless.h @@ -0,0 +1,101 @@ +// Headless C API for FinalBurn Neo. +// Author: Leonardo Kasperavičius +// +// Built originally to support OpenEmu-Silicon +// (https://github.com/OpenEmu-Silicon/OpenEmu-Silicon). +// +// Consumed by a thin host wrapper so the emulator-side glue stays in the fork. +// Subject to the FB Neo license (see src/license.txt). + +#ifndef BURNER_HEADLESS_H +#define BURNER_HEADLESS_H + +#ifdef __cplusplus +extern "C" { +#endif + +// Lifecycle +int FBHeadlessInit(void); +void FBHeadlessExit(void); +void FBHeadlessSetRomPath(const char *path); +int FBHeadlessLoadDriver(const char *shortName); // e.g. "mslug"; returns 0 on success +void FBHeadlessRunFrame(int draw); + +// Video (filled by intf/video/headless) +const unsigned char *FBHeadlessGetVideoBuffer(void); +int FBHeadlessGetVideoWidth(void); +int FBHeadlessGetVideoHeight(void); +int FBHeadlessGetVideoPitch(void); +int FBHeadlessGetVideoBPP(void); + +// Audio (filled each frame by the frontend) +int FBHeadlessGetAudioSampleRate(void); +const short *FBHeadlessGetAudioBuffer(void); +int FBHeadlessGetAudioFrames(void); // stereo sample frames produced last RunFrame + +// Input: value is non-zero when pressed. code is an FBNeo input switch code. +void FBHeadlessSetInputState(int code, int value); + +// Frame timing / geometry (valid after a driver is loaded). +int FBHeadlessGetFrameRate(void); // frames per second * 100 (e.g. 6000 = 60fps) +int FBHeadlessGetAspectX(void); +int FBHeadlessGetAspectY(void); + +// Reset the loaded driver. +void FBHeadlessReset(void); + +// Save/load a full machine state to/from a file. Returns 0 on success. +int FBHeadlessSaveState(const char *path); +int FBHeadlessLoadState(const char *path); + +// Memory access for cheats, cheat search, and achievements. Addresses are in the +// main CPU (cpu 0) hardware address space. size is 1..4 bytes; littleEndian +// controls byte order for multi-byte values. A value found via a memory-range +// snapshot can be written back at the same address with FBHeadlessWriteMemory. +unsigned int FBHeadlessGetMainMemorySize(void); // 0 until a driver is loaded +unsigned int FBHeadlessReadMemoryRange(unsigned int address, unsigned char *out, unsigned int length); // returns bytes read +unsigned int FBHeadlessReadMemory(unsigned int address, int size, int littleEndian); +int FBHeadlessWriteMemory(unsigned int address, unsigned int value, int size, int littleEndian); // 1 on success +int FBHeadlessIsAddressValid(unsigned int address); + +// Achievements memory. RetroAchievements sets are authored against FBNeo's flat +// main-RAM exposure (per driver hardware type), not the CPU address space. +// FBHeadlessCheevosInit rebuilds the map (call after load/reset); the read is +// gated to the mapped regions. Returns bytes read (stops at the first unmapped byte). +void FBHeadlessCheevosInit(void); +unsigned int FBHeadlessReadAchievementMemory(unsigned int address, unsigned char *out, unsigned int length); + +// Cheats: write into the achievement memory map (same flat RAM the search reads), and +// enumerate the mapped regions so the host can present cheat search. bigEndian follows +// the libretro/RetroAchievements convention the host matches: 0 (little-endian) for every +// board, including the 68K ones. See GetMainMemoryBigEndian. +int FBHeadlessWriteAchievementMemory(unsigned int address, unsigned int value, int size, int littleEndian); +int FBHeadlessGetMemoryRegionCount(void); +int FBHeadlessGetMemoryRegion(int index, unsigned int *start, unsigned int *length, int *bigEndian); + +// High-level input. player is 0-based; button is an FBHeadlessButton value. The +// fork maps it to the active driver's inputs (parsed from its input names). +typedef enum { + FBHeadlessButtonUp = 0, + FBHeadlessButtonDown, + FBHeadlessButtonLeft, + FBHeadlessButtonRight, + FBHeadlessButton1, + FBHeadlessButton2, + FBHeadlessButton3, + FBHeadlessButton4, + FBHeadlessButton5, + FBHeadlessButton6, + FBHeadlessButtonStart, + FBHeadlessButtonCoin, + FBHeadlessButtonService, // test switch (settings menu); "service" when there is none + FBHeadlessButtonConfigure, // "service" input of drivers that also have a test switch + FBHeadlessButtonCount +} FBHeadlessButton; +void FBHeadlessSetInput(int player, int button, int value); + +#ifdef __cplusplus +} +#endif + +#endif // BURNER_HEADLESS_H diff --git a/src/burner/headless/headless_drv.cpp b/src/burner/headless/headless_drv.cpp new file mode 100644 index 000000000..171596770 --- /dev/null +++ b/src/burner/headless/headless_drv.cpp @@ -0,0 +1,134 @@ +// Headless driver glue for FinalBurn Neo. +// Author: Leonardo Kasperavičius +// +// Built originally to support OpenEmu-Silicon +// (https://github.com/OpenEmu-Silicon/OpenEmu-Silicon). +// +// The headless counterpart of the SDL burner's drv.cpp: wraps BurnDrvInit in the +// zip loader and provides the frontend callbacks the shared burner code calls. +// CD systems (NeoGeo CD / PCE CD) are not wired yet. Subject to the FB Neo +// license (see src/license.txt). + +#include "burner.h" + +int bDrvOkay = 0; +char szAppRomPaths[DIRS_MAX][MAX_PATH] = { { "roms/" }, }; + +static bool bSaveRAM = false; + +// Battery-backed RAM (ACB_NVRAM: Mortal Kombat's settings, Robotron's high +// scores...) is not saved by the driver, unlike the EEPROM device. Like the +// Windows burner's StatedAuto, keep it in .fs: loaded +// after init and saved before exit. Drivers without NVRAM write no file. +static void DrvNvramAuto(int bSave) +{ + TCHAR szName[MAX_PATH]; + _stprintf(szName, _T("%s%s.fs"), szAppEEPROMPath, BurnDrvGetText(DRV_NAME)); + if (bSave) + BurnStateSave(szName, 0); + else + BurnStateLoad(szName, 0, NULL); +} + +static int DoLibInit() // Init the Burn library's driver +{ + BzipOpen(false); + int nRet = BurnDrvInit(); + BzipClose(); + return nRet ? 1 : 0; +} + +// Catch BurnLoadRom() calls once emulation has started: open the zip module, +// forward, then exit cleanly. +static int DrvLoadRom(unsigned char* Dest, int* pnWrote, int i) +{ + BzipOpen(false); + int nRet = BurnExtLoadRom(Dest, pnWrote, i); + BzipClose(); + + BurnExtLoadRom = DrvLoadRom; + return nRet; +} + +int DrvInit(int nDrvNum, bool bRestore) +{ + DrvExit(); // Make sure exited + MediaExit(); + + nBurnDrvActive = nDrvNum; + + { // Init input and audio; defer blitter init. + bVidOkay = 1; + bAudOkay = 1; + MediaInit(); + bVidOkay = 0; + bAudOkay = 0; + } + + nMaxPlayers = BurnDrvGetMaxPlayers(); + GameInpInit(); + + bSaveInputs = ConfigGameLoad(true); + if (bSaveInputs) ConfigGameLoadHardwareDefaults(); + + InputMake(true); + GameInpDefault(); + + if (DoLibInit()) { + BurnDrvExit(); + return 1; + } + + BurnExtLoadRom = DrvLoadRom; + + bDrvOkay = 1; + bSaveRAM = false; + nBurnLayer = 0xFF; + ConfigCheatLoad(); + RunReset(); + if (bRestore) { + DrvNvramAuto(0); + bSaveRAM = true; + } + return 0; +} + +int DrvInitCallback() +{ + return DrvInit(nBurnDrvActive, false); +} + +int DrvExit() +{ + if (bDrvOkay) { + if (nBurnDrvActive < nBurnDrvCount) { + if (bSaveRAM) { + DrvNvramAuto(1); + bSaveRAM = false; + } + ConfigGameSave(bSaveInputs); + GameInpExit(); + BurnDrvExit(); + } + } + + BurnExtLoadRom = NULL; + bDrvOkay = 0; + return 0; +} + +int ProgressUpdateBurner(double /* dProgress */, const TCHAR* /* pszText */, bool /* bAbs */) +{ + return 0; +} + +int AppError(TCHAR* /* szText */, int /* bWarning */) +{ + return 0; +} + +// The host drives frame timing, so there is no throttle to reset. +int RunReset() +{ + return 0; +} diff --git a/src/burner/headless/headless_input.cpp b/src/burner/headless/headless_input.cpp new file mode 100644 index 000000000..c4cc8dbe4 --- /dev/null +++ b/src/burner/headless/headless_input.cpp @@ -0,0 +1,209 @@ +// Headless input mapping for FinalBurn Neo. +// Author: Leonardo Kasperavičius +// +// Built originally to support OpenEmu-Silicon +// (https://github.com/OpenEmu-Silicon/OpenEmu-Silicon). +// +// Maps a (player, FBHeadlessButton) pair to the active driver's GameInp. FBNeo +// exposes each input's canonical lowercase name via szInfo ("p1 up", +// "p1 fire 1", "p1 coin", "p1 start"); each matched digital input is rebound to +// a private switch code the headless input plugin reads from its state table. +// Subject to the FB Neo license (see src/license.txt). + +#include "burner.h" +#include "fbheadless.h" + +// Private switch-code space, chosen to avoid the driver's default key/joy codes. +// code = HL_CODE_BASE + player*16 + button; unmatched digital inputs get a dead +// code that is never set so they read as released. +#define HL_CODE_BASE 0x100 +#define HL_CODE_DEAD 0x1FF + +static int hl_code(int player, int button) +{ + return HL_CODE_BASE + player * 16 + button; +} + +// Parse an FBNeo szInfo name into (player, FBHeadlessButton). Returns 1 on match. +static int hl_classify(const char *szInfo, int *outPlayer, int *outButton) +{ + if (szInfo == NULL) + return 0; + + // "pN " form (the common case). + if ((szInfo[0] == 'p' || szInfo[0] == 'P') && + szInfo[1] >= '1' && szInfo[1] <= '4' && szInfo[2] == ' ') { + int player = szInfo[1] - '1'; + const char *r = szInfo + 3; + int b = -1; + + if (!strcmp(r, "up")) b = FBHeadlessButtonUp; + else if (!strcmp(r, "down")) b = FBHeadlessButtonDown; + else if (!strcmp(r, "left")) b = FBHeadlessButtonLeft; + else if (!strcmp(r, "right")) b = FBHeadlessButtonRight; + else if (!strcmp(r, "start")) b = FBHeadlessButtonStart; + else if (!strcmp(r, "coin")) b = FBHeadlessButtonCoin; + else if (!strcmp(r, "service")) b = FBHeadlessButtonService; + else if (!strcmp(r, "diagnostic")) b = FBHeadlessButtonService; + else if (!strncmp(r, "fire ", 5) && r[5] >= '1' && r[5] <= '6') + b = FBHeadlessButton1 + (r[5] - '1'); + else if (!strncmp(r, "button ", 7) && r[7] >= '1' && r[7] <= '6') + b = FBHeadlessButton1 + (r[7] - '1'); + + if (b >= 0) { + *outPlayer = player; + *outButton = b; + return 1; + } + return 0; + } + + // System "coin N" / "start N" form. + if (!strncmp(szInfo, "coin ", 5) && szInfo[5] >= '1' && szInfo[5] <= '4') { + *outPlayer = szInfo[5] - '1'; + *outButton = FBHeadlessButtonCoin; + return 1; + } + if (!strncmp(szInfo, "start ", 6) && szInfo[6] >= '1' && szInfo[6] <= '4') { + *outPlayer = szInfo[6] - '1'; + *outButton = FBHeadlessButtonStart; + return 1; + } + + return 0; +} + +// Cabinet-wide test switch ("diag" in most drivers), which opens the game's +// settings menu. +static int hl_is_test_switch(const char *szInfo) +{ + return szInfo != NULL && + (!strcmp(szInfo, "diag") || !strcmp(szInfo, "diagnostic") || + !strcmp(szInfo, "diagnostics") || !strcmp(szInfo, "test")); +} + +// Cabinet-wide "service" input. Drivers with a separate test switch use it as a +// service credit; older drivers without one use it as the test switch itself. +static int hl_is_service_switch(const char *szInfo) +{ + return szInfo != NULL && !strcmp(szInfo, "service"); +} + +// Index of a direction word at the start of s (0 left, 1 up, 2 down, 3 right), +// or -1. *len receives its length. +static int hl_direction(const char *s, size_t *len) +{ + static const char *const dirs[] = { "left", "up", "down", "right" }; + for (int d = 0; d < 4; d++) { + size_t n = strlen(dirs[d]); + if (!strncasecmp(s, dirs[d], n)) { + *len = n; + return d; + } + } + return -1; +} + +// Twin-stick games (Robotron, Smash TV, Angel Kids, Ikari Warriors...), mapped +// the way the MAME core does: the left stick on the player's d-pad, the right +// stick on buttons 1-4 (left, up, down, right), since OpenEmu's arcade +// controller has one stick. Drivers name the sticks inconsistently: the second +// stick may be "pN 2", or either stick may borrow another player's plain +// d-pad ("p2 up" for Robotron's "Fire Up", "p3 up" for Angel Kids' "P1 Left +// Stick Up"). The display name, when it names a stick, decides the stick and +// the player; otherwise "pN 2" is player N's right stick. +static int hl_twin_stick(const char *szName, const char *szInfo, int *outPlayer, int *outButton) +{ + if (szName == NULL || szInfo == NULL) + return 0; + if (szInfo[0] != 'p' || szInfo[1] < '1' || szInfo[1] > '4' || szInfo[2] != ' ') + return 0; + + size_t len = 0; + int dir = hl_direction(szInfo + 3, &len); + if (dir < 0) + return 0; + const char *rest = szInfo + 3 + len; + int secondStick = !strcmp(rest, " 2"); + if (!secondStick && *rest != '\0') + return 0; + + static const struct { const char *prefix; int right; } sticks[] = { + { "Left Stick ", 0 }, { "Lft Stick ", 0 }, { "L Stick ", 0 }, { "Left ", 0 }, + { "Right Stick ", 1 }, { "Rght Stick ", 1 }, { "ight Stick ", 1 }, { "R Stick ", 1 }, + { "Right ", 1 }, { "Fire ", 1 }, { "Shoot ", 1 }, { "Aim ", 1 }, + }; + static const int dpad[] = { FBHeadlessButtonLeft, FBHeadlessButtonUp, FBHeadlessButtonDown, FBHeadlessButtonRight }; + + const char *n = szName; + int player = 0; + if ((n[0] == 'P' || n[0] == 'p') && n[1] >= '1' && n[1] <= '4' && n[2] == ' ') { + player = n[1] - '1'; + n += 3; + } + for (size_t s = 0; s < sizeof(sticks) / sizeof(sticks[0]); s++) { + size_t sl = strlen(sticks[s].prefix); + size_t nameDirLen = 0; + if (strncasecmp(n, sticks[s].prefix, sl) != 0) + continue; + if (hl_direction(n + sl, &nameDirLen) != dir || n[sl + nameDirLen] != '\0') + break; + *outPlayer = player; + *outButton = sticks[s].right ? FBHeadlessButton1 + dir : dpad[dir]; + return 1; + } + + if (secondStick) { + *outPlayer = szInfo[1] - '1'; + *outButton = FBHeadlessButton1 + dir; + return 1; + } + return 0; +} + +// Rebind every digital input of the loaded driver to the headless code space. +// Called after DrvInit (which runs GameInpInit/GameInpDefault). +void headless_map_inputs(void) +{ + struct BurnInputInfo bii; + + // Service always opens the settings menu: it drives the test switch when the + // driver has one (and Configure drives "service"), otherwise "service". + int hasTestSwitch = 0; + for (UINT32 i = 0; i < nGameInpCount; i++) { + if (BurnDrvGetInputInfo(&bii, i) == 0 && bii.nType == BIT_DIGITAL && hl_is_test_switch(bii.szInfo)) { + hasTestSwitch = 1; + break; + } + } + + for (UINT32 i = 0; i < nGameInpCount; i++) { + if (BurnDrvGetInputInfo(&bii, i)) + continue; + if (bii.nType != BIT_DIGITAL) + continue; + + GameInp[i].nInput = GIT_SWITCH; + + int player = 0, button = 0; + if (hl_is_test_switch(bii.szInfo)) + GameInp[i].Input.Switch.nCode = (UINT16)hl_code(0, FBHeadlessButtonService); + else if (hl_is_service_switch(bii.szInfo)) + GameInp[i].Input.Switch.nCode = (UINT16)hl_code(0, hasTestSwitch ? FBHeadlessButtonConfigure : FBHeadlessButtonService); + else if (hl_twin_stick(bii.szName, bii.szInfo, &player, &button)) + GameInp[i].Input.Switch.nCode = (UINT16)hl_code(player, button); + else if (hl_classify(bii.szInfo, &player, &button) && player >= 0 && player < 4) + GameInp[i].Input.Switch.nCode = (UINT16)hl_code(player, button); + else + GameInp[i].Input.Switch.nCode = HL_CODE_DEAD; + } +} + +extern "C" void FBHeadlessSetInput(int player, int button, int value) +{ + if (player < 0 || player >= 4) + return; + if (button < 0 || button >= FBHeadlessButtonCount) + return; + FBHeadlessSetInputState(hl_code(player, button), value); +} diff --git a/src/burner/headless/headless_main.cpp b/src/burner/headless/headless_main.cpp new file mode 100644 index 000000000..ac4b2f5b4 --- /dev/null +++ b/src/burner/headless/headless_main.cpp @@ -0,0 +1,192 @@ +// Headless frontend for FinalBurn Neo. +// Author: Leonardo Kasperavičius +// +// Built originally to support OpenEmu-Silicon +// (https://github.com/OpenEmu-Silicon/OpenEmu-Silicon). +// +// Provides the burner-side globals the FBNeo library expects from a frontend and +// a small C API the host drives. Video/audio/input are routed through the +// intf/{video,audio,input}/headless plugins; driver init/exit lives in drv.cpp. +// +// Subject to the FB Neo license (see src/license.txt). + +#include "burner.h" +#include "fbheadless.h" + +// Implemented in headless_input.cpp. +void headless_map_inputs(void); + +// Implemented in vid_headless.cpp (rotates the native frame to the display buffer). +void headless_video_present(void); + +// --- Burner globals expected by the FBNeo library / shared burner code --- +int nAppVirtualFps = 6000; // app fps * 100 +bool bRunPause = 0; +bool bAppDoFast = 0; +char fpsstring[20] = ""; +bool bAppShowFPS = 0; +bool bAlwaysProcessKeyboardInput = 0; +bool bAppFullscreen = 0; +bool bIntegerScale = false; +int nWindowScale = 2; +bool bAlwaysMenu = false; +int nGameSelect = 0; +int nFilterSelect = 0; +bool bShowAvailableOnly = true; +bool bShowClones = true; +int gameSelectedFromFilter = -1; +UINT32 messageFrames = 0; +char lastMessage[MESSAGE_MAX_LENGTH] = ""; +int bDrvSaveAll = 0; +TCHAR szAppBurnVer[16] = _T(""); + +// --- Frontend audio state --- +static short *audBuffer = NULL; +static int audFrames = 0; // stereo sample frames produced last frame +static int audSampleRate = 48000; + +static int allocAudio() +{ + free(audBuffer); + audBuffer = NULL; + if (nBurnSoundLen <= 0) + return 1; + audBuffer = (short *)malloc((nBurnSoundLen * 2 + 16) * sizeof(short)); + if (audBuffer == NULL) + return 1; + memset(audBuffer, 0, (nBurnSoundLen * 2 + 16) * sizeof(short)); + return 0; +} + +// --- C API --- + +int FBHeadlessInit(void) +{ + BurnLibInit(); + return 0; +} + +void FBHeadlessExit(void) +{ + DrvExit(); + VidExit(); + AudSoundExit(); + InputExit(); + free(audBuffer); + audBuffer = NULL; + BurnLibExit(); +} + +void FBHeadlessSetRomPath(const char *path) +{ + // BzipOpen builds zip paths as szAppRomPaths[d] + ".zip" with no + // separator, so the path must end with a slash. + size_t n = strlen(path); + if (n > 0 && path[n - 1] == '/') + snprintf(szAppRomPaths[0], MAX_PATH, "%s", path); + else + snprintf(szAppRomPaths[0], MAX_PATH, "%s/", path); +} + +int FBHeadlessLoadDriver(const char *shortName) +{ + UINT32 i; + for (i = 0; i < nBurnDrvCount; i++) { + nBurnDrvActive = i; + if (strcmp(BurnDrvGetTextA(DRV_NAME), shortName) == 0) + break; + } + if (i >= nBurnDrvCount) + return 1; + + nAudSampleRate[0] = audSampleRate; + nBurnSoundRate = audSampleRate; + + InputInit(); + AudSoundInit(); + + if (DrvInit(i, true)) // true: restore battery-backed RAM + return 1; + + VidInit(); // after DrvInit: the game's visible size is known + headless_map_inputs(); + + nAppVirtualFps = nBurnFPS; + if (nAppVirtualFps <= 0) + nAppVirtualFps = 6000; + nBurnSoundLen = (audSampleRate * 100 + (nAppVirtualFps >> 1)) / nAppVirtualFps; + allocAudio(); + + FBHeadlessCheevosInit(); // build the achievement memory map + return 0; +} + +void FBHeadlessRunFrame(int draw) +{ + if (!bDrvOkay) + return; + + InputMake(true); + + pBurnDraw = draw ? pVidImage : NULL; + pBurnSoundOut = audBuffer; + + BurnDrvFrame(); + + if (draw) + headless_video_present(); + + audFrames = nBurnSoundLen; +} + +// Audio accessors +int FBHeadlessGetAudioSampleRate(void) { return audSampleRate; } +const short *FBHeadlessGetAudioBuffer(void) { return audBuffer; } +int FBHeadlessGetAudioFrames(void) { return audFrames; } + +// Frame timing / geometry +int FBHeadlessGetFrameRate(void) { return nBurnFPS; } + +int FBHeadlessGetAspectX(void) +{ + INT32 x = 0, y = 0; + if (bDrvOkay) + BurnDrvGetAspect(&x, &y); + return x; +} + +int FBHeadlessGetAspectY(void) +{ + INT32 x = 0, y = 0; + if (bDrvOkay) + BurnDrvGetAspect(&x, &y); + return y; +} + +void FBHeadlessReset(void) +{ + if (!bDrvOkay) + return; + DrvInit(nBurnDrvActive, true); // full re-init; DrvExit saves battery RAM, DrvInit restores it + VidInit(); // DrvInit defers blitter init + frees buffers; recreate them + headless_map_inputs(); + FBHeadlessCheevosInit(); // RAM buffers were reallocated; refresh the map +} + +int FBHeadlessSaveState(const char *path) +{ + if (!bDrvOkay || path == NULL) + return 1; + TCHAR szName[1024]; + ANSIToTCHAR(path, szName, 1024); + return BurnStateSave(szName, 1); // bAll=1: full state +} + +int FBHeadlessLoadState(const char *path) +{ + if (!bDrvOkay || path == NULL) + return 1; + TCHAR szName[1024]; + ANSIToTCHAR(path, szName, 1024); + return BurnStateLoad(szName, 1, NULL); // game already loaded, so no reload callback +} diff --git a/src/burner/headless/headless_memory.cpp b/src/burner/headless/headless_memory.cpp new file mode 100644 index 000000000..6ff14b857 --- /dev/null +++ b/src/burner/headless/headless_memory.cpp @@ -0,0 +1,217 @@ +// Memory access for cheats, cheat search, and achievements (FinalBurn Neo headless). +// Author: Leonardo Kasperavičius +// +// Built originally to support OpenEmu-Silicon +// (https://github.com/OpenEmu-Silicon/OpenEmu-Silicon). +// +// Thin C wrappers over FBNeo's cheat-engine memory interface (src/burn/cheat.cpp). +// All addresses are in the main CPU (cpu 0) hardware address space — the same +// space FBNeo's own cheat and achievement support uses, so a value found by a +// memory search can be written back by address. +// +// Subject to the FB Neo license (see src/license.txt). + +#include "burner.h" +#include "state.h" // BurnArea, BurnAreaScan, BurnAcb, ACB_FULLSCAN +#include "fbheadless.h" + +unsigned int FBHeadlessGetMainMemorySize(void) +{ + return GetMainMemorySize(); +} + +unsigned int FBHeadlessReadMemoryRange(unsigned int address, unsigned char *out, unsigned int length) +{ + return ReadMemoryRange(address, out, length); +} + +// Standard byte order: byte i lives at address+i. littleEndian controls which end +// is the least-significant byte. (FBNeo's own Read/WriteValueAtHardwareAddress use +// an MSB-first, address-decrementing convention — not exposed here to avoid surprises.) +unsigned int FBHeadlessReadMemory(unsigned int address, int size, int littleEndian) +{ + if (!bDrvOkay || size < 1 || size > 4) + return 0; + unsigned char b[4] = { 0, 0, 0, 0 }; + ReadMemoryRange(address, b, (unsigned int)size); + unsigned int value = 0; + for (int i = 0; i < size; i++) { + int shift = littleEndian ? (8 * i) : (8 * (size - 1 - i)); + value |= (unsigned int)b[i] << shift; + } + return value; +} + +int FBHeadlessWriteMemory(unsigned int address, unsigned int value, int size, int littleEndian) +{ + if (!bDrvOkay || size < 1 || size > 4) + return 0; + for (int i = 0; i < size; i++) { + int shift = littleEndian ? (8 * i) : (8 * (size - 1 - i)); + unsigned char byte = (value >> shift) & 0xFF; + WriteValueAtHardwareAddress(address + i, byte, 1, 1); // single byte, no endian ambiguity + } + return 1; +} + +int FBHeadlessIsAddressValid(unsigned int address) +{ + return IsHardwareAddressValid(address) ? 1 : 0; +} + +// --- Achievements memory map --- +// +// RetroAchievements sets are authored against the flat main-RAM buffer FBNeo's +// libretro port exposes (see CheevosInit / StateGetMainRamAcb in retro_memory.cpp), +// NOT the CPU hardware address space. Find the same RAM area(s) by driver hardware +// type and expose them at the same RA base addresses the libretro port uses. + +#define FBH_MAX_RA_REGIONS 10 +typedef struct { unsigned int start; unsigned int len; unsigned char *ptr; } FBHRaRegion; +static FBHRaRegion s_raRegions[FBH_MAX_RA_REGIONS]; +static int s_raRegionCount = 0; +static int s_raBigEndian = 0; // main CPU multibyte order (68K etc. = big-endian) + +static void fbh_ra_add(unsigned int start, unsigned int len, void *ptr) +{ + if (s_raRegionCount >= FBH_MAX_RA_REGIONS || ptr == NULL || len == 0) + return; + s_raRegions[s_raRegionCount].start = start; + s_raRegions[s_raRegionCount].len = len; + s_raRegions[s_raRegionCount].ptr = (unsigned char *)ptr; + s_raRegionCount++; +} + +static INT32 __cdecl fbh_ra_acb(struct BurnArea *pba) +{ + if (!pba->szName) + return 0; + + switch (BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) { + case HARDWARE_CAPCOM_CPS1: + case HARDWARE_CAPCOM_CPS1_QSOUND: + case HARDWARE_CAPCOM_CPS1_GENERIC: + case HARDWARE_CAPCOM_CPSCHANGER: + case HARDWARE_CAPCOM_CPS2: + if (strcmp(pba->szName, "CpsRamFF") == 0) fbh_ra_add(0, pba->nLen, pba->Data); + return 0; + case HARDWARE_CAPCOM_CPS3: + if (strcmp(pba->szName, "Main RAM") == 0) fbh_ra_add(0, pba->nLen, pba->Data); + return 0; + case HARDWARE_SNK_NEOCD: + if (strcmp(pba->szName, "68K program RAM") == 0) fbh_ra_add(0x00000000, pba->nLen, pba->Data); + if (strcmp(pba->szName, "Memory card") == 0) fbh_ra_add(0x00800000, pba->nLen, pba->Data); + return 0; + case HARDWARE_SNK_NEOGEO: + case HARDWARE_IGS_PGM: + if (strcmp(pba->szName, "68K RAM") == 0) fbh_ra_add(0, pba->nLen, pba->Data); + return 0; + case HARDWARE_PSIKYO: + if (strcmp(pba->szName, "All RAM") == 0 || strcmp(pba->szName, "68K RAM") == 0) fbh_ra_add(0, pba->nLen, pba->Data); + return 0; + case HARDWARE_CAVE_68K_Z80: + case HARDWARE_CAVE_68K_ONLY: + if (strcmp(pba->szName, "RAM") == 0 || strcmp(pba->szName, "68K RAM") == 0) fbh_ra_add(0, pba->nLen, pba->Data); + return 0; + case HARDWARE_SEGA_MEGADRIVE: + if (strcmp(pba->szName, "RAM") == 0) fbh_ra_add(0x00FF0000, pba->nLen, pba->Data); + if (strcmp(pba->szName, "NV RAM") == 0) fbh_ra_add(0x00000000, pba->nLen, pba->Data); + return 0; + case HARDWARE_SEGA_MASTER_SYSTEM: + case HARDWARE_SEGA_GAME_GEAR: + if (strcmp(pba->szName, "sms") == 0) fbh_ra_add(0, pba->nLen, pba->Data); + return 0; + case HARDWARE_NES: + case HARDWARE_FDS: + case HARDWARE_NVS: + if (strcmp(pba->szName, "CPU Ram") == 0) fbh_ra_add(0x00000000, pba->nLen, pba->Data); + if (strcmp(pba->szName, "Work Ram") == 0) fbh_ra_add(0x00006000, pba->nLen, pba->Data); + return 0; + case HARDWARE_SNK_NGP: + case HARDWARE_SNK_NGPC: + if (strcmp(pba->szName, "Main Ram") == 0) fbh_ra_add(0x00004000, pba->nLen, pba->Data); + if (strcmp(pba->szName, "Shared Ram") == 0) fbh_ra_add(0x00007000, pba->nLen, pba->Data); + return 0; + case HARDWARE_TOAPLAN_RAIZING: + if (strcmp(pba->szName, "All Ram") == 0 || strcmp(pba->szName, "All RAM") == 0 || strcmp(pba->szName, "RAM") == 0) fbh_ra_add(0, pba->nLen, pba->Data); + return 0; + default: + // Most other drivers expose main RAM as "All Ram" / "All RAM". + if (strcmp(pba->szName, "All Ram") == 0 || strcmp(pba->szName, "All RAM") == 0) fbh_ra_add(0, pba->nLen, pba->Data); + return 0; + } +} + +// Rebuild the achievement memory map. Call after a driver loads or resets, since +// DrvInit reallocates the RAM buffers (the cached pointers would otherwise dangle). +void FBHeadlessCheevosInit(void) +{ + INT32 nMin = 0; + s_raRegionCount = 0; + s_raBigEndian = 0; + if (!bDrvOkay) + return; + s_raBigEndian = GetMainMemoryBigEndian(); + BurnAcb = fbh_ra_acb; + BurnAreaScan(ACB_FULLSCAN, &nMin); +} + +unsigned int FBHeadlessReadAchievementMemory(unsigned int address, unsigned char *out, unsigned int length) +{ + if (out == NULL) + return 0; + + unsigned int i; + for (i = 0; i < length; i++) { + unsigned int a = address + i; + int found = 0; + for (int r = 0; r < s_raRegionCount; r++) { + if (a >= s_raRegions[r].start && a < s_raRegions[r].start + s_raRegions[r].len) { + out[i] = s_raRegions[r].ptr[a - s_raRegions[r].start]; + found = 1; + break; + } + } + if (!found) + return i; // stop at the first unmapped address + } + return length; +} + +// Write a value into the achievement memory map (used to apply cheats). Bytes land +// at address+i for littleEndian, or MSB-first for big-endian. Returns 1 if anything +// was written. Direct buffer writes — the CPU sees them next frame (plain RAM). +int FBHeadlessWriteAchievementMemory(unsigned int address, unsigned int value, int size, int littleEndian) +{ + if (size < 1 || size > 4) + return 0; + int wrote = 0; + for (int i = 0; i < size; i++) { + unsigned int a = littleEndian ? (address + i) : (address + (size - 1 - i)); + unsigned char byte = (value >> (8 * i)) & 0xFF; + for (int r = 0; r < s_raRegionCount; r++) { + if (a >= s_raRegions[r].start && a < s_raRegions[r].start + s_raRegions[r].len) { + s_raRegions[r].ptr[a - s_raRegions[r].start] = byte; + wrote = 1; + break; + } + } + } + return wrote; +} + +// Enumerate the mapped regions so the host can present cheat-search memory. +int FBHeadlessGetMemoryRegionCount(void) +{ + return s_raRegionCount; +} + +int FBHeadlessGetMemoryRegion(int index, unsigned int *start, unsigned int *length, int *bigEndian) +{ + if (index < 0 || index >= s_raRegionCount) + return 0; + if (start) *start = s_raRegions[index].start; + if (length) *length = s_raRegions[index].len; + if (bigEndian) *bigEndian = s_raBigEndian; + return 1; +} diff --git a/src/burner/headless/headless_misc.cpp b/src/burner/headless/headless_misc.cpp new file mode 100644 index 000000000..9bd2f6b15 --- /dev/null +++ b/src/burner/headless/headless_misc.cpp @@ -0,0 +1,49 @@ +// Headless frontend helpers for FinalBurn Neo. +// Author: Leonardo Kasperavičius +// +// Built originally to support OpenEmu-Silicon +// (https://github.com/OpenEmu-Silicon/OpenEmu-Silicon). +// +// Small frontend functions/globals the shared burner code expects, provided by +// each platform (the headless counterpart of burner/macos/misc.cpp). Subject to +// the FB Neo license (see src/license.txt). + +#include "burner.h" + +// General-purpose debug global referenced by various drivers. +int counter = 0; + +TCHAR* ANSIToTCHAR(const char* pszInString, TCHAR* pszOutString, int /* nOutSize */) +{ + if (pszOutString) { + _tcscpy(pszOutString, pszInString); + return pszOutString; + } + return (TCHAR*)pszInString; +} + +char* TCHARToANSI(const TCHAR* pszInString, char* pszOutString, int /* nOutSize */) +{ + if (pszOutString) { + _tcscpy(pszOutString, pszInString); + return pszOutString; + } + return (char*)pszInString; +} + +TCHAR* AdaptiveEncodingReads(const TCHAR* /* pszFileName */) +{ + return NULL; +} + +bool AppProcessKeyboardInput() +{ + // Input is always live for the headless core (the host gates pause/focus), + // and CinpState() zeroes keyboard-range codes (< 0x4000, which ours are) + // unless this returns true. + return true; +} + +void UpdateMessage(char* /* message */) +{ +} diff --git a/src/burner/headless/tchar.h b/src/burner/headless/tchar.h new file mode 100644 index 000000000..2e388807d --- /dev/null +++ b/src/burner/headless/tchar.h @@ -0,0 +1,72 @@ +#ifndef TCHAR_H +#define TCHAR_H + +// Always available so non-_UNICODE code that still handles wide buffers +// (e.g. burn.cpp zip-name parsing) can call wcslen/wcscpy. +#include + +#ifdef _UNICODE + +#include + +#define __TEXT(q) L##q + +typedef wchar_t TCHAR; +typedef wchar_t _TCHAR; + +#else /* ifndef _UNICODE */ + +#define __TEXT(q) q + +typedef char TCHAR; +typedef char _TCHAR; + +//#define wcslen(void) + +#define _tcslen strlen +#define _tcscpy strcpy +#define _tcsncpy strncpy + +#define _tprintf printf +#define _vstprintf vsprintf +#define _vsntprintf vsnprintf +#define _stprintf sprintf +#define _sntprintf snprintf +#define _ftprintf fprintf +#define _tsprintf sprintf + +#define _tcscmp strcmp +#define _tcsncmp strncmp +#define _tcsicmp strcasecmp +#define _tcsnicmp strncasecmp +#define _tcstol strtol +#define _tcsrchr strrchr +#define _tcsstr strstr + +#define _tcsstr strstr + +#define _fgetts fgets +#define _fputts fputs + +#define _istspace isspace + +#define _tfopen fopen + +#define _stricmp strcmp +#define _strnicmp strncmp + +// Win32 string/char shims the shared code uses on POSIX builds. +#include +#define stricmp strcasecmp +#define strnicmp strncasecmp +#define _totlower(c) tolower((unsigned char)(c)) +#define _totupper(c) toupper((unsigned char)(c)) + +#define dprintf(...) fprintf (stderr, __VA_ARGS__) + +#endif + +#define _TEXT(x) __TEXT(x) +#define _T(x) __TEXT(x) + +#endif diff --git a/src/intf/audio/aud_interface.cpp b/src/intf/audio/aud_interface.cpp index b831bfdf2..a1e59348e 100644 --- a/src/intf/audio/aud_interface.cpp +++ b/src/intf/audio/aud_interface.cpp @@ -22,6 +22,8 @@ static UINT32 nAudActive = 0; extern struct AudOut AudOutWASAPI; #elif defined (BUILD_MACOS) extern struct AudOut AudOutMacOS; +#elif defined (BUILD_HEADLESS) + extern struct AudOut AudOutHeadless; #elif defined (BUILD_SDL) extern struct AudOut AudOutSDL; #elif defined (BUILD_SDL2) @@ -47,6 +49,8 @@ static struct AudOut *pAudOut[]= &AudOutWASAPI, #elif defined (BUILD_MACOS) &AudOutMacOS, +#elif defined (BUILD_HEADLESS) + &AudOutHeadless, #elif defined (BUILD_SDL) &AudOutSDL, #elif defined (BUILD_SDL2) diff --git a/src/intf/audio/headless/aud_headless.cpp b/src/intf/audio/headless/aud_headless.cpp new file mode 100644 index 000000000..a42593168 --- /dev/null +++ b/src/intf/audio/headless/aud_headless.cpp @@ -0,0 +1,49 @@ +// Headless audio output for FinalBurn Neo. +// Author: Leonardo Kasperavičius +// +// Built originally to support OpenEmu-Silicon +// (https://github.com/OpenEmu-Silicon/OpenEmu-Silicon). +// +// The frontend drives sound directly (it points pBurnSoundOut at its own buffer +// and reads it after BurnDrvFrame), so this plugin only satisfies the AudOut +// interface. Subject to the FB Neo license (see src/license.txt). + +#include "burner.h" + +static int HeadlessSoundBlank() { return 0; } +static int HeadlessSoundCheck() { return 0; } + +static int HeadlessSoundInit() +{ + if (nAudSampleRate[0] <= 0) + return 1; + bAudOkay = 1; + return 0; +} + +static int HeadlessSetCallback(int (* /* pCallback */)(int)) { return 0; } +static int HeadlessSoundPlay() { bAudPlaying = 1; return 0; } +static int HeadlessSoundStop() { bAudPlaying = 0; return 0; } + +static int HeadlessSoundExit() +{ + bAudOkay = 0; + bAudPlaying = 0; + return 0; +} + +static int HeadlessSoundSetVolume() { return 0; } +static int HeadlessSoundGetSettings(InterfaceInfo *) { return 0; } + +struct AudOut AudOutHeadless = { + HeadlessSoundBlank, + HeadlessSoundCheck, + HeadlessSoundInit, + HeadlessSetCallback, + HeadlessSoundPlay, + HeadlessSoundStop, + HeadlessSoundExit, + HeadlessSoundSetVolume, + HeadlessSoundGetSettings, + _T("Headless audio output"), +}; diff --git a/src/intf/input/headless/inp_headless.cpp b/src/intf/input/headless/inp_headless.cpp new file mode 100644 index 000000000..02d2caf46 --- /dev/null +++ b/src/intf/input/headless/inp_headless.cpp @@ -0,0 +1,63 @@ +// Headless input for FinalBurn Neo. +// Author: Leonardo Kasperavičius +// +// Built originally to support OpenEmu-Silicon +// (https://github.com/OpenEmu-Silicon/OpenEmu-Silicon). +// +// Reads a digital switch state table populated by the host via +// FBHeadlessSetInputState(). InputMake() calls ReadSwitch() while building each +// frame's GameInp. Subject to the FB Neo license (see src/license.txt). + +#include "burner.h" +#include "fbheadless.h" + +// Indexed by FBNeo input switch code (masked); 0 = released, non-zero = pressed. +#define HEADLESS_INPUT_TABLE_SIZE 0x200 +static unsigned char inputState[HEADLESS_INPUT_TABLE_SIZE]; + +static int HeadlessInpInit() +{ + memset(inputState, 0, sizeof(inputState)); + bInputOkay = 1; + return 0; +} + +static int HeadlessInpExit() +{ + bInputOkay = 0; + return 0; +} + +static int HeadlessInpSetCooperativeLevel(bool /* bExclusive */, bool /* bForeground */) { return 0; } +static int HeadlessInpNewFrame() { return 0; } + +static int HeadlessInpReadSwitch(int nCode) +{ + return inputState[nCode & (HEADLESS_INPUT_TABLE_SIZE - 1)] ? 1 : 0; +} + +static int HeadlessInpReadJoyAxis(int /* i */, int /* nAxis */) { return 0; } +static int HeadlessInpReadMouseAxis(int /* i */, int /* nAxis */) { return 0; } +static int HeadlessInpFind(bool /* createBaseline */) { return -1; } + +static int HeadlessInpGetControlName(int /* nCode */, TCHAR * /* dev */, TCHAR * /* ctrl */) { return 0; } +static int HeadlessInpGetSettings(InterfaceInfo *) { return 0; } + +struct InputInOut InputInOutHeadless = { + HeadlessInpInit, + HeadlessInpExit, + HeadlessInpSetCooperativeLevel, + HeadlessInpNewFrame, + HeadlessInpReadSwitch, + HeadlessInpReadJoyAxis, + HeadlessInpReadMouseAxis, + HeadlessInpFind, + HeadlessInpGetControlName, + HeadlessInpGetSettings, + _T("Headless input"), +}; + +extern "C" void FBHeadlessSetInputState(int code, int value) +{ + inputState[code & (HEADLESS_INPUT_TABLE_SIZE - 1)] = value ? 1 : 0; +} diff --git a/src/intf/input/inp_interface.cpp b/src/intf/input/inp_interface.cpp index dc6159b27..212aac546 100644 --- a/src/intf/input/inp_interface.cpp +++ b/src/intf/input/inp_interface.cpp @@ -16,6 +16,8 @@ static bool bCinpOkay; extern struct InputInOut InputInOutXInput; #elif defined (BUILD_MACOS) extern struct InputInOut InputInOutMacOS; +#elif defined (BUILD_HEADLESS) + extern struct InputInOut InputInOutHeadless; #elif defined (BUILD_SDL) extern struct InputInOut InputInOutSDL; #elif defined (BUILD_SDL2) @@ -33,6 +35,8 @@ static struct InputInOut *pInputInOut[]= &InputInOutXInput, #elif defined (BUILD_MACOS) &InputInOutMacOS, +#elif defined (BUILD_HEADLESS) + &InputInOutHeadless, #elif defined (BUILD_SDL2) &InputInOutSDL2, #elif defined (BUILD_SDL) diff --git a/src/intf/video/headless/vid_headless.cpp b/src/intf/video/headless/vid_headless.cpp new file mode 100644 index 000000000..3d7d6114c --- /dev/null +++ b/src/intf/video/headless/vid_headless.cpp @@ -0,0 +1,192 @@ +// Headless video output for FinalBurn Neo. +// Author: Leonardo Kasperavičius +// +// Adapted from the macOS video interface by Akop Karapetyan +// https://github.com/0xe1f/FinalBurn-X (Apache License 2.0) +// +// Built originally to support OpenEmu-Silicon +// (https://github.com/OpenEmu-Silicon/OpenEmu-Silicon). +// +// FBNeo renders every game in its native orientation (vertical games come out +// sideways), so this keeps a native render buffer for the emulator and presents +// an upright, correctly-pitched buffer to the host, rotating when needed. +// Subject to the FB Neo license (see src/license.txt). + +#include "burner.h" +#include "fbheadless.h" + +static unsigned char *nativeBuffer = NULL; // FBNeo renders here (pBurnDraw), nativeBpp deep +static unsigned char *displayBuffer = NULL; // exposed to the host, always 32bpp upright +static int nativeW = 0, nativeH = 0; +static int dispW = 0, dispH = 0; +static int nativeBpp = 0; // FBNeo render depth in bytes: 2 (BDF_16BIT_ONLY) or 4 +static int rotate = 0; // 0 none, 1 CW90, 2 180, 3 CCW90 + +static void freeBuffers() +{ + if (displayBuffer && displayBuffer != nativeBuffer) + free(displayBuffer); + free(nativeBuffer); + nativeBuffer = NULL; + displayBuffer = NULL; +} + +static int HeadlessVideoInit() +{ + int vw = 0, vh = 0; + int flags = BurnDrvGetFlags(); + BurnDrvGetVisibleSize(&vw, &vh); + + // Some drivers (Cave, CPS3, Megadrive) are BDF_16BIT_ONLY: their blitters only + // write RGB565, so render those at 16bpp and convert to 32bpp on present. All + // others render straight 32bpp XRGB8888, which the host consumes directly. + if (flags & BDF_16BIT_ONLY) { + nativeBpp = 2; + nVidImageDepth = 16; + } else { + nativeBpp = 4; + nVidImageDepth = 32; + } + nVidImageBPP = nativeBpp; + SetBurnHighCol(nVidImageDepth); + + if (flags & BDF_ORIENTATION_VERTICAL) { + // Visible size is the upright (display) size; FBNeo renders it sideways. + nativeW = vh; nativeH = vw; + dispW = vw; dispH = vh; + rotate = (flags & BDF_ORIENTATION_FLIPPED) ? 1 : 3; // CW : CCW + } else { + nativeW = vw; nativeH = vh; + dispW = vw; dispH = vh; + rotate = (flags & BDF_ORIENTATION_FLIPPED) ? 2 : 0; // 180 : none + } + + freeBuffers(); + nativeBuffer = (unsigned char *)malloc(nativeW * nativeH * nativeBpp); + if (nativeBuffer == NULL) + return 1; + memset(nativeBuffer, 0, nativeW * nativeH * nativeBpp); + + // The host always receives 32bpp. Zero-copy only when FBNeo already renders + // 32bpp AND upright; otherwise present() fills a dedicated 32bpp buffer + // (converting 16->32 and/or rotating). + if (nativeBpp == 4 && rotate == 0) { + displayBuffer = nativeBuffer; + } else { + displayBuffer = (unsigned char *)malloc(dispW * dispH * 4); + if (displayBuffer == NULL) + return 1; + memset(displayBuffer, 0, dispW * dispH * 4); + } + + // FBNeo draws into the native buffer, pitch = native width. + nVidImageWidth = nativeW; + nVidImageHeight = nativeH; + nVidImagePitch = nativeW * nativeBpp; + nBurnBpp = nativeBpp; + nBurnPitch = nativeW * nativeBpp; + pVidImage = nativeBuffer; + + return 0; +} + +static int HeadlessVideoExit() +{ + freeBuffers(); + nativeW = nativeH = dispW = dispH = nativeBpp = rotate = 0; + return 0; +} + +// RGB565 -> 0x00RRGGBB, matching SetBurnHighCol(32)'s component order. +static inline uint32_t rgb565_to_xrgb8888(uint16_t p) +{ + uint32_t r = (p >> 11) & 0x1F; + uint32_t g = (p >> 5) & 0x3F; + uint32_t b = p & 0x1F; + r = (r << 3) | (r >> 2); + g = (g << 2) | (g >> 4); + b = (b << 3) | (b >> 2); + return (r << 16) | (g << 8) | b; +} + +// Map a display pixel (dx,dy) back to its native-buffer coordinate for the +// current rotation. Native/display dims are swapped for the 90-degree cases. +static inline void src_coord(int dx, int dy, int *sx, int *sy) +{ + switch (rotate) { + case 1: *sx = dy; *sy = nativeH - 1 - dx; break; // CW 90 + case 3: *sx = nativeW - 1 - dy; *sy = dx; break; // CCW 90 + case 2: *sx = dispW - 1 - dx; *sy = dispH - 1 - dy; break; // 180 + default: *sx = dx; *sy = dy; break; // none + } +} + +// Present the freshly rendered native frame to the host: convert 16->32bpp for +// BDF_16BIT_ONLY drivers and/or rotate vertical games upright. Called by the +// frontend after BurnDrvFrame. The zero-copy case (32bpp, upright) is a no-op. +void headless_video_present(void) +{ + if (displayBuffer == nativeBuffer) + return; + + uint32_t *dst = (uint32_t *)displayBuffer; + + if (nativeBpp == 2) { + const uint16_t *src = (const uint16_t *)nativeBuffer; + for (int dy = 0; dy < dispH; dy++) + for (int dx = 0; dx < dispW; dx++) { + int sx, sy; + src_coord(dx, dy, &sx, &sy); + dst[dy * dispW + dx] = rgb565_to_xrgb8888(src[sy * nativeW + sx]); + } + } else { + const uint32_t *src = (const uint32_t *)nativeBuffer; + for (int dy = 0; dy < dispH; dy++) + for (int dx = 0; dx < dispW; dx++) { + int sx, sy; + src_coord(dx, dy, &sx, &sy); + dst[dy * dispW + dx] = src[sy * nativeW + sx]; + } + } +} + +static int HeadlessVideoFrame(bool redraw) +{ + if (pVidImage == NULL || bRunPause) + return 0; + VidFrameCallback(redraw); + return 0; +} + +static int HeadlessVideoPaint(int /* validate */) +{ + return 0; +} + +static int HeadlessVideoScale(RECT *, int, int) +{ + return 0; +} + +static int HeadlessVideoGetSettings(InterfaceInfo *) +{ + return 0; +} + +struct VidOut VidOutHeadless = { + HeadlessVideoInit, + HeadlessVideoExit, + HeadlessVideoFrame, + HeadlessVideoPaint, + HeadlessVideoScale, + HeadlessVideoGetSettings, + _T("Headless Video"), +}; + +// --- C accessors for the host wrapper --- + +extern "C" const unsigned char *FBHeadlessGetVideoBuffer(void) { return displayBuffer; } +extern "C" int FBHeadlessGetVideoWidth(void) { return dispW; } +extern "C" int FBHeadlessGetVideoHeight(void) { return dispH; } +extern "C" int FBHeadlessGetVideoPitch(void) { return dispW * 4; } +extern "C" int FBHeadlessGetVideoBPP(void) { return 4; } diff --git a/src/intf/video/vid_interface.cpp b/src/intf/video/vid_interface.cpp index b6209d5fe..2c40137ca 100644 --- a/src/intf/video/vid_interface.cpp +++ b/src/intf/video/vid_interface.cpp @@ -15,6 +15,8 @@ extern struct VidOut VidOutD3D11; #elif defined (BUILD_MACOS) extern struct VidOut VidOutMacOS; +#elif defined (BUILD_HEADLESS) + extern struct VidOut VidOutHeadless; #elif defined (BUILD_PI) extern struct VidOut VidOutPi; #elif defined (BUILD_SDL2) @@ -39,6 +41,8 @@ static struct VidOut *pVidOut[] = { &VidOutD3D11, #elif defined (BUILD_MACOS) &VidOutMacOS, +#elif defined (BUILD_HEADLESS) + &VidOutHeadless, #elif defined (BUILD_PI) &VidOutPi, #elif defined (BUILD_SDL2) From afa75aa877de82006961902b25212710e429c22c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Kasperavi=C4=8Dius?= Date: Thu, 29 Oct 2026 01:34:25 +0100 Subject: [PATCH 24/26] Fix noise when playing games like TMNT (#2842) TmntDecodeTitleSample did `val <<= (expo-3)` where expo-3 is often negative; a negative left-shift is undefined behaviour and on arm64 produced garbage spikes, turning the digitized title theme into noise. Shift right for the negative case. --- src/burn/drv/konami/d_tmnt.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/burn/drv/konami/d_tmnt.cpp b/src/burn/drv/konami/d_tmnt.cpp index e821bd00f..7dfa9a712 100644 --- a/src/burn/drv/konami/d_tmnt.cpp +++ b/src/burn/drv/konami/d_tmnt.cpp @@ -4590,7 +4590,10 @@ static void TmntDecodeTitleSample() val = (val >> 3) & (0x3ff); val -= 0x200; - val <<= (expo-3); + // expo-3 can be negative; a negative left-shift is undefined behaviour + // (produced garbage spikes on arm64). Shift right for the negative case. + INT32 sh = expo - 3; + if (sh >= 0) val <<= sh; else val >>= (-sh); DrvTitleSample[i] = val; } @@ -5892,7 +5895,7 @@ static void RenderTitleSample(INT16 *pSoundBuf, INT32 nLength) { double Addr = TitleSamplePos; double Step = (double)20000 / nBurnSoundRate; - + for (INT32 i = 0; i < nLength; i += 2) { if (Addr > 0x3ffff) break; INT16 Sample = DrvTitleSample[(INT32)Addr]; From f41c6b4619fcbc53f19940b1072f6fc61c77619b Mon Sep 17 00:00:00 2001 From: Falc0n24 Date: Thu, 29 Oct 2026 01:53:28 +0100 Subject: [PATCH 25/26] burn: copy driver names only when they are changed (#2843) BurnGameListInit() gave every driver writable copies of its names at startup: 100 + MAX_PATH bytes plus MAX_PATH wide characters, all zero-filled, for each of the ~28,700 drivers. That is about 39 MiB of dirty heap in every FBNeo session, although names are only changed for the loaded driver (RomData, sub-driver and custom names, localisation). On a 256 MiB handheld it is pushed to swap at every game launch. Create the writable copy on the first change of a driver's name instead, and point the driver back at its built-in names on exit (the old exit left them pointing at freed buffers, which a second BurnLibInit() then copied from). Name setters also stop at the buffer size now. --- src/burn/burn.cpp | 121 ++++++++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 52 deletions(-) diff --git a/src/burn/burn.cpp b/src/burn/burn.cpp index 669983b51..a3b4221eb 100644 --- a/src/burn/burn.cpp +++ b/src/burn/burn.cpp @@ -90,8 +90,51 @@ bool bSaveCRoms = 0; UINT32 *pBurnDrvPalette; -static char** pszShortName = NULL, ** pszFullNameA = NULL; -static wchar_t** pszFullNameW = NULL; +// Writable driver names, created only for drivers whose name is changed +// (RomData, custom/sub-driver names, localisation). Copying all of them at +// startup cost about 1.4 KiB per driver: about 39 MiB for 28,000 drivers. +struct BurnNameCopy { + UINT32 nDrv; + char* szOrigShortName; + char* szOrigFullNameA; + wchar_t* szOrigFullNameW; + char szShortName[100]; + char szFullNameA[MAX_PATH]; +#if defined (_UNICODE) + wchar_t szFullNameW[MAX_PATH]; +#endif + BurnNameCopy* pNext; +}; +static BurnNameCopy* pBurnNameCopies = NULL; + +static BurnNameCopy* BurnNameCopyGet(UINT32 i) +{ + if (i >= nBurnDrvCount) return NULL; + + for (BurnNameCopy* p = pBurnNameCopies; p; p = p->pNext) { + if (p->nDrv == i) return p; + } + + BurnNameCopy* p = (BurnNameCopy*)calloc(1, sizeof(BurnNameCopy)); + if (NULL == p) return NULL; + + p->nDrv = i; + p->szOrigShortName = pDriver[i]->szShortName; + p->szOrigFullNameA = pDriver[i]->szFullNameA; + p->szOrigFullNameW = pDriver[i]->szFullNameW; + if (pDriver[i]->szShortName) strncpy(p->szShortName, pDriver[i]->szShortName, sizeof(p->szShortName) - 1); + if (pDriver[i]->szFullNameA) strncpy(p->szFullNameA, pDriver[i]->szFullNameA, MAX_PATH - 1); + pDriver[i]->szShortName = p->szShortName; + pDriver[i]->szFullNameA = p->szFullNameA; +#if defined (_UNICODE) + if (pDriver[i]->szFullNameW) wcsncpy(p->szFullNameW, pDriver[i]->szFullNameW, MAX_PATH - 1); + pDriver[i]->szFullNameW = p->szFullNameW; +#endif + p->pNext = pBurnNameCopies; + pBurnNameCopies = p; + + return p; +} bool BurnCheckMMXSupport() { @@ -107,53 +150,20 @@ bool BurnCheckMMXSupport() } static void BurnGameListInit() -{ // Avoid broken references, RomData requires separate string storage - if (0 == nBurnDrvCount) return; - - pszShortName = (char** )malloc(nBurnDrvCount * sizeof(char*)); - pszFullNameA = (char** )malloc(nBurnDrvCount * sizeof(char*)); - pszFullNameW = (wchar_t**)malloc(nBurnDrvCount * sizeof(wchar_t*)); - - if ((NULL != pszShortName) && (NULL != pszFullNameA) && (NULL != pszFullNameW)) { - for (UINT32 i = 0; i < nBurnDrvCount; i++) { - pszShortName[i] = (char* )malloc(100 * sizeof(char)); - pszFullNameA[i] = (char* )malloc(MAX_PATH * sizeof(char)); - pszFullNameW[i] = (wchar_t*)malloc(MAX_PATH * sizeof(wchar_t)); - - memset(pszShortName[i], '\0', 100 * sizeof(char)); - memset(pszFullNameA[i], '\0', MAX_PATH * sizeof(char)); - memset(pszFullNameW[i], '\0', MAX_PATH * sizeof(wchar_t)); - - if (NULL != pszShortName[i]) { - strcpy(pszShortName[i], pDriver[i]->szShortName); - pDriver[i]->szShortName = pszShortName[i]; - } - if (NULL != pszFullNameA[i]) { - strcpy(pszFullNameA[i], pDriver[i]->szFullNameA); - pDriver[i]->szFullNameA = pszFullNameA[i]; - } -#if defined (_UNICODE) - if (NULL != pDriver[i]->szFullNameW) { - wmemcpy(pszFullNameW[i], pDriver[i]->szFullNameW, MAX_PATH); // Include '\0' - } - pDriver[i]->szFullNameW = pszFullNameW[i]; -#endif - } - } - +{ // Names are copied on first change, see BurnNameCopyGet(). } static void BurnGameListExit() { - // Release of storage space - for (UINT32 i = 0; i < nBurnDrvCount; i++) { - if ((NULL != pszShortName) && (NULL != pszShortName[i])) free(pszShortName[i]); - if ((NULL != pszFullNameA) && (NULL != pszFullNameA[i])) free(pszFullNameA[i]); - if ((NULL != pszFullNameW) && (NULL != pszFullNameW[i])) free(pszFullNameW[i]); + // Point the drivers back at their built-in names, then release the copies. + while (pBurnNameCopies) { + BurnNameCopy* p = pBurnNameCopies; + pDriver[p->nDrv]->szShortName = p->szOrigShortName; + pDriver[p->nDrv]->szFullNameA = p->szOrigFullNameA; + pDriver[p->nDrv]->szFullNameW = p->szOrigFullNameW; + pBurnNameCopies = p->pNext; + free(p); } - if (NULL != pszShortName) free(pszShortName); - if (NULL != pszFullNameA) free(pszFullNameA); - if (NULL != pszFullNameW) free(pszFullNameW); } extern "C" INT32 BurnLibInit() @@ -519,9 +529,10 @@ static INT32 BurnDrvSetFullNameA(char* szName, UINT32 i = nBurnDrvActive) { // Preventing the emergence of ~0U // If not NULL, then FullNameA is customized - if ((i >= 0) && (NULL != szName)) { - memset(pszFullNameA[i], '\0', MAX_PATH * sizeof(char)); - strcpy(pszFullNameA[i], szName); + BurnNameCopy* p = (NULL != szName) ? BurnNameCopyGet(i) : NULL; + if (NULL != p) { + memset(p->szFullNameA, '\0', MAX_PATH * sizeof(char)); + strncpy(p->szFullNameA, szName, MAX_PATH - 1); return 0; } @@ -534,8 +545,10 @@ INT32 BurnDrvSetFullNameW(TCHAR* szName, INT32 i = nBurnDrvActive) if ((-1 == i) || (NULL == szName)) return -1; #if defined (_UNICODE) - memset(pszFullNameW[i], '\0', MAX_PATH * sizeof(wchar_t)); - wcscpy(pszFullNameW[i], szName); + BurnNameCopy* p = BurnNameCopyGet(i); + if (NULL == p) return -1; + memset(p->szFullNameW, '\0', MAX_PATH * sizeof(wchar_t)); + wcsncpy(p->szFullNameW, szName, MAX_PATH - 1); #endif return 0; @@ -548,8 +561,10 @@ void BurnLocalisationSetName(char* szName, TCHAR* szLongName) nBurnDrvActive = i; if (!strcmp(szName, pDriver[i]->szShortName)) { // pDriver[i]->szFullNameW = szLongName; - memset(pszFullNameW[i], '\0', MAX_PATH * sizeof(wchar_t)); - _tcscpy(pszFullNameW[i], szLongName); + BurnNameCopy* p = BurnNameCopyGet(i); + if (NULL == p) continue; + memset(p->szFullNameW, '\0', MAX_PATH * sizeof(wchar_t)); + wcsncpy(p->szFullNameW, szLongName, MAX_PATH - 1); } } } @@ -618,7 +633,9 @@ extern "C" INT32 BurnDrvSetZipName(char* szName, INT32 i) { if ((NULL == szName) || (-1 == i)) return -1; - strcpy(pszShortName[i], szName); + BurnNameCopy* p = BurnNameCopyGet((UINT32)i); + if (NULL == p) return -1; + strncpy(p->szShortName, szName, sizeof(p->szShortName) - 1); return 0; } From 2ee74794038e6997d4e917c9c584a47c7d6b9a39 Mon Sep 17 00:00:00 2001 From: Squirbie Date: Thu, 29 Oct 2026 19:09:34 +0900 Subject: [PATCH 26/26] d_legionna: fix Grainbow P3/P4 inputs (#2845) --- src/burn/drv/pst90s/d_legionna.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/burn/drv/pst90s/d_legionna.cpp b/src/burn/drv/pst90s/d_legionna.cpp index a66e8b2c7..5d66cc5c1 100644 --- a/src/burn/drv/pst90s/d_legionna.cpp +++ b/src/burn/drv/pst90s/d_legionna.cpp @@ -221,7 +221,7 @@ static struct BurnInputInfo GrainbowInputList[] = { {"P2 Button 2", BIT_DIGITAL, DrvJoy1 + 13, "p2 fire 2" }, {"P2 Button 3", BIT_DIGITAL, DrvJoy1 + 14, "p2 fire 3" }, - {"P3 Start", BIT_DIGITAL, DrvJoy1 + 8, "p3 start" }, + {"P3 Start", BIT_DIGITAL, DrvJoy3 + 8, "p3 start" }, {"P3 Up", BIT_DIGITAL, DrvJoy2 + 0, "p3 up" }, {"P3 Down", BIT_DIGITAL, DrvJoy2 + 1, "p3 down" }, {"P3 Left", BIT_DIGITAL, DrvJoy2 + 2, "p3 left" }, @@ -230,7 +230,7 @@ static struct BurnInputInfo GrainbowInputList[] = { {"P3 Button 2", BIT_DIGITAL, DrvJoy2 + 5, "p3 fire 2" }, {"P3 Button 3", BIT_DIGITAL, DrvJoy2 + 6, "p3 fire 3" }, - {"P4 Start", BIT_DIGITAL, DrvJoy1 + 9, "p4 start" }, + {"P4 Start", BIT_DIGITAL, DrvJoy3 + 9, "p4 start" }, {"P4 Up", BIT_DIGITAL, DrvJoy2 + 8, "p4 up" }, {"P4 Down", BIT_DIGITAL, DrvJoy2 + 9, "p4 down" }, {"P4 Left", BIT_DIGITAL, DrvJoy2 + 10, "p4 left" },