65.9K
CodeProject 正在变化。 阅读更多。
Home

MAME.NET 的主要架构

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (51投票s)

2019年2月1日

CPOL

13分钟阅读

viewsIcon

129167

downloadIcon

4642

C# 街机模拟器, ROM 修改

MAME.NET 用户界面

引言

MAME (Multiple Arcade Machine Emulator,中文意为“多街机模拟器”) 是一款免费开源的模拟器,旨在在现代个人电脑和其他平台上通过软件重现街机游戏系统的硬件。MAME.NET 是一款基于 C# 的街机模拟器,它保持了 MAME 的相同架构。通过使用 C# 和强大的集成开发环境——Microsoft Visual Studio,无需宏,您可以随时调试支持的街机游戏。目前支持一些经典街机主板:M72、M92、Taito、Taito B、Capcom、CPS-1、CPS-1(Qsound)、CPS2、Data East、Tehkan、Konami 68000、Neo Geo、Namco System 1、IGS011、PGM(PolyGame Master)。

MAME.NET 按以下步骤运行:加载 ROMs,初始化机器,软重置机器,然后循环执行 cpuexec_timeslice 操作。cpuexec_timeslice 操作意味着按顺序为每个 CPU 执行一个时间片,并执行定时器回调。定时器回调包括:视频更新、软重置、CPU 中断、声音更新、看门狗重置和其他中断。通过这些步骤,MAME.NET 成功地模拟了街机主板。MAME.NET 还有更多功能:保存和加载状态、录制和重播输入、作弊、作弊搜索、IPS (补丁主 ROM)、主板调试器、CPU 调试器。

加载 ROMs

作为一个模拟器,MAME.NET 首先加载 ROMs。

Irem M72 主板上有两个 CPU:一个 NEC V30 CPU 和一个 Zilog Z80 CPU。V30 具有 20 位地址和 16 位数据。Z80 具有 16 位地址和 8 位数据。V30 CPU 以 8MHz 运行。程序将文件 maincpu.rom 加载为 V30 ROM。Z80 CPU 以 3579545 Hz 运行。程序将文件 soundcpu.rom 加载为 Z80 ROM。有两个声卡:Yamaha YM2151、DAC 芯片。

Irem M92 主板上有两个 CPU:一个 NEC V33 CPU 和一个 NEC V30 CPU。两个 CPU 都具有 20 位地址和 16 位数据。V33 CPU 以 9MHz 运行。程序将文件 maincpu.rom 加载为 V33 ROM。V30 CPU 以 7159090 Hz 运行。程序将文件 soundcpu.rom 加载为 V30 ROM。有两个声卡:Yamaha YM2151、Irem GA20。

对于 Taito 主板,有各种 CPU 和芯片设置。

对于 Taito B 主板,M68000 CPU 以 12MHz 运行(某些游戏为 16MHz)。程序将文件 maincpu.rom 加载为 M68000 ROM。文件 gfx1.rom 包含显示在屏幕上的瓦片数据。Z80 CPU 以 4MHz 运行。程序将文件 audiocpu.rom 加载为 Z80 ROM。有两个声卡:General Instrument AY8910、Yamaha YM2610。程序将文件 ymsnd.rom 和可选文件 ymsnddeltat.rom 加载为 Yamaha YM2610 ROM。

对于 Capcom 主板,有各种 CPU 和芯片设置。

CPS-1、CPS-1(Qsound)、CPS2、Neo Geo、PGM 主板上有 2 个 CPU:一个 Motorola M68000 CPU 和一个 Zilog Z80 CPU。M68000 具有 24 位地址和 16 位数据。Z80 具有 16 位地址和 8 位数据。

对于 CPS-1 主板,M68000 CPU 以 10MHz 运行(某些游戏为 12MHz)。程序将文件 maincpu.rom 加载为 M68000 ROM。maincpu.rom 的大小不应超过 0x400000 字节。文件 gfx.rom 包含显示在屏幕上的瓦片数据。Z80 CPU 以 3579545 Hz 运行。程序将文件 audiocpu.rom 加载为 Z80 ROM。audiocpu.rom 的大小不应超过 0x18000 字节。有两个声卡:Yamaha YM2151、Oki MSM6295。程序将文件 oki.rom 加载为 Oki MSM6295 ROM。

对于 CPS-1(QSound) 主板,M68000 CPU 以 12MHz 运行。程序将文件 maincpu.rom 加载为 M68000 ROM。maincpu.rom 的大小不应超过 0x400000 字节。文件 gfx.rom 包含显示在屏幕上的瓦片数据。Z80 CPU 以 8MHz 运行,并具有 Kabuki 加密代码。程序将文件 audiocpu.rom 加载为 Z80 ROM 进行 ReadMemory 操作,将文件 audiocpuop.rom 加载为 Z80 ROM 进行 ReadOp 操作。audiocpu.rom 的大小不应超过 0x50000 字节。有一个 Q-Sound 芯片。程序将文件 qsound.rom 加载为 Q-Sound 芯片 ROM。

对于 CPS2 主板,M68000 CPU 以 11.8MHz 运行。原始 M68000 ROM 已加密。程序将文件 maincpu.rom 加载为 M68000 ROM 用于内存代码,将文件 maincpuop.rom 加载为 M68000 ROM 用于操作码。文件 gfx.rom 包含显示在屏幕上的瓦片数据。Z80 CPU 以 8MHz 运行。程序将文件 audiocpu.rom 加载为 Z80 ROM。audiocpu.rom 的大小不应超过 0x50000 字节。有一个 Q-Sound 芯片。程序将文件 qsound.rom 加载为 Q-Sound 芯片 ROM。

对于 Data East 主板,主 M6502 CPU 以 2MHz 运行,声音 M6502 CPU 以 1.5MHz 运行。文件 gfx1.rom, gfx2.rom 包含显示在屏幕上的瓦片数据。有四个声卡:General Instrument AY8910、Yamaha YM2203、Yamaha YM3812、Oki MSM5205。

对于 Tehkan 主板,有各种 CPU 和芯片设置。

对于 Konami 68000 主板,有各种 CPU 和芯片设置。有 Konami 图形芯片:K052109、K051960、K053245、K054000、K053936 和 K053251。有相关的声卡:Yamaha YM2151、Konami K007232、Konami K053260、Konami K054539、NEC UPD7759。

对于 Neo Geo 主板,M68000 CPU 以 12MHz 运行。程序将文件 maincpu.rom 加载为 M68000 ROM。文件 sprites.rom 包含显示在屏幕上的瓦片数据。文件 fixed.rom 是图形相关的 ROM。Z80 CPU 以 4MHz 运行。程序将文件 audiocpu.rom 加载为 Z80 ROM。有两个声卡:General Instrument AY8910、Yamaha YM2610。程序将文件 ymsnd.rom 和可选文件 ymsnddeltat.rom 加载为 Yamaha YM2610 ROM。

对于 PGM 主板,M68000 CPU 以 20MHz 运行。程序将文件 maincpu.rom 加载为 M68000 ROM。文件 sprcol.romsprmask.romtiles.rom 包含显示在屏幕上的瓦片数据。程序动态写入 Z80 ROM。有一个 ICS2115 声卡。

Namco System 1 主板上有四个 CPU:三个 Motorola M6809 CPU 和一个 Hitachi HD63701 CPU。四个 CPU 都具有 16 位地址和 8 位数据。

对于 Namco System 1,四个 CPU 以 1536000Hz 运行。Namco Custom 117 MMU 为第一个和第二个 M6809 CPU 提供 23 位虚拟地址。程序将文件 user1.rom 加载为两个 CPU ROM。文件 gfx1.romgfx2.romgfx3.rom 包含显示在屏幕上的瓦片数据。程序将文件 audiocpu.rom 加载为第三个 M6809 CPU ROM。程序将文件 voice.rom 加载为 HD63701 CPU ROM。有三个声卡:Yamaha YM2151、Namco 8 声道立体声芯片、DAC 芯片。

对于 IGS011 主板,M68000 CPU 以 7333333Hz 运行。程序将文件 maincpu.rom 加载为 M68000 ROM。文件 gfx1.rom 包含显示在屏幕上的瓦片数据。有两个声卡:Oki MSM6295 和 Yamaha YM3812。程序将文件 oki.rom 加载为 Oki MSM6295 ROM。

MAME.NET 的 ROM 格式是最简单的。您可以使用 IDA Pro 直接打开并反汇编 maincpu.romaudiocpu.rom (仅限非操作码加密)。没有多个 ROM 的组合,没有图形效果解码,没有各种解码,也没有字节序颠倒。因此,IPS 文件(.cht 扩展名,与作弊文件相同)易于理解。ROM 黑客可以专注于真实的地址-值对,而忽略 ROM 的编码和解码。您还可以使用 MAME.NET 中的 M68000 调试器和 Z80 调试器功能来反汇编 CPU ROM。

常见用法

构建环境:我仅在 Windows 10、Microsoft Visual Studio 2008 上进行测试

运行环境:Microsoft .NET Framework 3.5 或更高版本

热键:F3 -- 软重置,F7 -- 加载状态,Shift+F7 -- 保存状态,F8 -- 重播输入,Shift+F8 -- 录制输入(开始和停止),状态相关热键后跟 0-9 和 A-Z -- 处理特定文件,F10 -- 切换全局节流,P -- 暂停和继续,Shift+P - 跳过一帧

控制键

  • 1 -- P1 开始
  • 2 -- P2 开始
  • 5 -- P1 投币
  • 6 -- P2 投币
  • R -- 服务 1
  • T -- 服务
  • W -- P1 上
  • S -- P1 下
  • A -- P1 左
  • D -- P1 右
  • J -- P1 按钮 1
  • K -- P1 按钮 2
  • L -- P1 按钮 3
  • U -- P1 按钮 4
  • I -- P1 按钮 5
  • O -- P1 按钮 6
  • 上 -- P2 上
  • 下 -- P2 下
  • 左 -- P2 左
  • 右 -- P2 右
  • 小键盘 1 -- P2 按钮 1
  • 小键盘 2 -- P2 按钮 2
  • 小键盘 3 -- P2 按钮 3
  • 小键盘 4 -- P2 按钮 4
  • 小键盘 5 -- P2 按钮 5
  • 小键盘 6 -- P2 按钮 6

支持鼠标的游戏:Operation Wolf。

加载游戏 ROMs 后,模拟器会自动暂停。您现在可以应用 IPS 和 DIP 开关,然后按 P 继续。您可以从运行的 MAME 中获取正确的 DIP 开关值。

偶尔会发生 GDI+ 错误并显示一个红叉。您可以点击 文件-重置图片框 来处理该错误。

您可以参考 MAME 或其他模拟器的作弊文件来制作 MAME.NET 作弊文件。对于某些模拟器(例如:Winkawaks),您应该将 ^1 操作应用于作弊地址。

有两个文件用于录制输入。.sta 文件记录初始状态,.inp 文件记录输入按键。

您可以将多个 .inp 文件合并到一个 .inp 文件中。例如,在目录 "inp\pcktgal\" 中有 5 个录制的输入(0.sta4.sta0.inp 到 4.inp)。请记住,在两个录制输入之间不要输入任何控制键。将文件 "0.sta" 复制到 "q.sta" 作为初始状态。然后依次将文件 "0.inp"、"1.inp"… "4.inp" 连接到一个文件 "q.inp" 中。这样,“q” 的录制输入就是 “0”、“1”……“4” 录制输入的组合。您可以通过组合多个片段轻松录制最佳输入。Youtube 链接:https://www.youtube.com/watch?v=5ve9w-1K1j0

下载 pcktgal.mp4

ROM 修改

您可以使用 MAME.NET 的 M68000 调试器和 Z80 调试器功能有效地修改 ROM。对于初学者来说,M68000 ROM 修改包含以下步骤:确定关键的 mainram 地址和值,反向调试 M68000 CPU 直到获得确定的 mainrom 地址和值,然后修补对象 mainrom。现在我展示一个例子。

1. 吞食天地(Warriors of Fate),卡萨尔一直坐着

卡萨尔一直坐着

录制 1.inp 进行研究,2.inp 显示结果。

以下是游戏 wof 的准备知识。每个精灵都有一个在 mainram 中的基地址,并且接下来的 0xE0 字节描述了精灵的状态。偏移量 0x28、0x29、0x2A、0x2B 描述了精灵的子程序。偏移量 0x82、0x83 描述了精灵的生命值。对于 1.inp 场景,卡萨尔的基地址是 0xBE1C,Basl 的基地址是 0xCF9C。在检查 mainram[0xBE1C+0x2B] 的变化后,我发现如果在我抓住敌人时禁用写入 0x08 到 mainram[0xBE1C+0x2B](保持原始值 0x06),卡萨尔就会一直坐着。

修改源代码

为了修改 ROM,我找到了 M68000totalExecutedCycles 对象,其值为 0x262318F0,并反汇编了附近的 L 代码。

26231866 0016B8: 6706                  beq      $16C0
26231870 0016C0: 0C69 0000 0002        cmpi.w   $0, ($2,A1)
26231880 0016C6: 6612                  bne      $16DA
2623188A 0016DA: 9169 0082             sub.w    D0, ($82,A1)
2623189A 0016DE: 4A29 00C7             tst.b    ($C7,A1)
262318A6 0016E2: 6B1C                  bmi      $1700
262318B0 001700: 4E75                  rts      
262318C0 01B706: 317C 038E 0048        move.w   $38E, ($48,A0)
262318D0 01B70C: 317C FFC8 004A        move.w   $FFC8, ($4A,A0)
262318E0 01B712: 3B7C 0016 FF9E        move.w   $16, ($FF9E,A5)

262318F0 01B718: 117C 0008 002B        move.b   $8, ($2B,A0)
26231900 01B71E: 4EF8 5F7A             jmp      ($5F7A)

2623190A 005F7A: 4A2D 0190             tst.b    ($190,A5)

这里 A0=FFFFBE1C(卡萨尔的基地址),A1=FFFFCF9C(被抓住敌人的基地址)。现在我将在 maincpu.rom 的偏移量 0x01B718 处插入新的程序。

0x1B718: jmp address1
address1: tst.b ($82,A1)        //examine the blood of caught enemy
bmi address2
jmp      ($5F7A)                //if blood is non-negative, disable the writing 
                                //of mainram[0xCF9C+0x2B] and jump directly

address2: move.b   $8, ($2B,A0) //if blood is negative, write mainram[0xCF9C+0x2B] 
                                //to 0x08 as normal
jmp      ($5F7A)

maincpu.rom 中,从 0xE68400xE6E40 有一个大的 0x00 数据块。所以我将 address1 指定为 0xE6880,将 address2 指定为 0xE6890

01B718:	4EF9 000E 6880        jmp ($E6880)
0E6880: 4A29 0082             tst.b ($82,A1)
0E6884: 6B0A                  bmi ($E6890)
0E6886:	4EF9 0000 5F7A        jmp      ($5F7A)

0E6890: 117C 0008 002B        move.b   $8, ($2B,A0)
0E6896: 4EF9 0000 5F7A        jmp      ($5F7A)

因此,我得到了以下作弊代码

[Kassar sits continuously]
ON=1B718,4E;1B719,F9;1B71A,00;1B71B,0E;1B71C,68;1B71D,80;E6880,4A;
E6881,29;E6882,00;E6883,82;E6884,6B;E6885,0A;E6886,4E;E6887,F9;
E6888,00;E6889,00;E688A,5F;E688B,7A;E6890,11;E6891,7C;E6892,00;
E6893,08;E6894,00;E6895,2B;E6896,4E;E6897,F9;E6898,00;E6899,00;E689A,5F;E689B,7A

2.inp 的修改效果

2. 饿狼传说 2(Samurai Shodown II),选择隐藏角色

参考 samsho2.xml 作弊文件,我得到了以下代码

	 <cheat desc="Select Character PL1"> <comment>Free player selection. 
      Activate between rounds or just after selection.</comment>
                   <parameter>
                            <item value="00">Off</item>
                            <item value="01">Haohmaru</item>
                            <item value="02">Nakoruru</item>
                            <item value="03">Hanzo</item>
                            <item value="04">Galford</item>
                            <item value="05">Wan-Fu</item>
                            <item value="06">Ukyo</item>
                            <item value="07">Kyoshiro</item>
                            <item value="08">Gen-An</item>
                            <item value="09">Earthquake</item>
                            <item value="10">Jubei</item>
                            <item value="11">Charlotte</item>
                            <item value="12">Genjuro</item>
                            <item value="13">Cham Cham</item>
                            <item value="14">Sieger</item>
                            <item value="15">Nicotine</item>
                            <item value="16">Mizuki</item>
                            <item value="17">Kuroko</item>
                   </parameter>
                   <script state="run">
                            <action condition="(param==01)">main.pb@100D0B=00</action>
                            <action condition="(param==02)">main.pb@100D0B=01</action>
                            <action condition="(param==03)">main.pb@100D0B=02</action>
                            <action condition="(param==04)">main.pb@100D0B=03</action>
                            <action condition="(param==05)">main.pb@100D0B=04</action>
                            <action condition="(param==06)">main.pb@100D0B=05</action>
                            <action condition="(param==07)">main.pb@100D0B=06</action>
                            <action condition="(param==08)">main.pb@100D0B=07</action>
                            <action condition="(param==09)">main.pb@100D0B=08</action>
                            <action condition="(param==10)">main.pb@100D0B=09</action>
                            <action condition="(param==11)">main.pb@100D0B=0B</action>
                            <action condition="(param==12)">main.pb@100D0B=0C</action>
                            <action condition="(param==13)">main.pb@100D0B=0D</action>
                            <action condition="(param==14)">main.pb@100D0B=0E</action>
                            <action condition="(param==15)">main.pb@100D0B=0F</action>
                            <action condition="(param==16)">main.pb@100D0B=10</action>
                            <action condition="(param==17)">main.pb@100D0B=11</action>
                   </script>
         </cheat>

我将豪鬼(Haohmaru)缩写为 c00,娜可露露(Nakoruru)缩写为 c01……角色在选择场景中的排列如下

选择场景
character id:
03 0e 06 00 0c 02 0b 04
 07 01 09 05 0d 08 0f

有两个隐藏角色:c10c11。我将修改 ROM 以显示它们。

重播 1 的录制输入。找出 M68000 代码何时将 mainram[0xD0B] 写入 0x06,然后反向调试直到找到对象 mainrom 的位置。

6E3D35F 01474A: 1030 0000             move.b   (A0,D0.w), D0 D0=00100004->00100006 
                                                          A0=000142CC mainrom[0x142D0]=0x06
6E3D36D 01474E: 0240 00FF             andi.w   $FF, D0
6E3D375 014752: 1540 000B             move.b   D0, ($B,A2) D0=00100006 A2=00100D00 
                                                           mainram[0xD0B]=0x06

我检查 maincpu.rom 文件在偏移量 0x142CC 处,并发现了以下代码

主 CPU ROM

标记的代码与角色 ID 相关。角色 ID 的基地址是 0x000142CC

我将把选择场景扩展到

character id:
 03 0e 06 00 0c 02 0b 04
11 07 01 09 05 0d 08 0f 10

我找到了以下读取地址 0x000142CC 的代码。

657F8E0 0141E4: 49F9 0001 42CC        lea      $142CC.l, A4
657F8EC 0141EA: 7A00                  moveq    $0, D5
657F8F0 0141EC: 7C0E                  moveq    $E, D6

maincpu ROM 中从 0x000142CC0x000142CB 的数据替换为。因为要添加两个角色,所以将 $E 替换为 $10

我从 PPC 01474A 开始反向调试

6590B24 01471A: 720F                  moveq    $F, D1
6590B28 01471C: 0240 000F             andi.w   $F, D0 D0=00105200->00100000
6590B30 014720: 41F9 0001 478A        lea      $1478A.l, A0
6590B3C 014726: 1030 0000             move.b   (A0,D0.w), D0 D0=00100000 A0=0001478A
6590B4A 01472A: 1400                  move.b   D0, D2 D2=53540000
6590B4E 01472C: 4880                  ext.w    D0 
6590B52 01472E: D051                  add.w    (A1), D0 D0=00100000->00100006 
                                                     A1=00100C00 mainram[0xC00]=0x0006
6590B5A 014730: 6B00 0004             bmi      $14736
6590B66 014734: 4441                  neg.w    D1 D1=0000000F->0000FFF1
6590B6A 014736: 0C40 000E             cmpi.w   $E, D0 D0=00100006
6590B72 01473A: 6300 0004             bls      $14740
6590B7C 014740: 3280                  move.w   D0, (A1) A1=00100C00 mainram[0xC00]=0006
6590B84 014742: 41F9 0001 42CC        lea      $142CC.l, A0
6590B90 014748: 3011                  move.w   (A1), D0 D0=00100006
6590B98 01474A: 1030 0000             move.b   (A0,D0.w), D0 D0=00100006->00100000 
                                                          A0=000142CC mainrom[0x142D2]=0x00
6590BA6 01474E: 0240 00FF             andi.w   $FF, D0
6590BAE 014752: 1540 000B             move.b   D0, ($B,A2) D0=00100000 A2=00100D00 
                                               mainram[0xD0B]=0x00

因为要添加两个角色,所以将数据 $F 替换为 $11,将 $E 替换为 $10

因此,我得到了以下作弊代码

[17 select 1]
ON=1387D,CB;141E9,CB;141ED,10;142CB,11;142DB,10;1471B,11;14739,10;14747,CB

应用上述作弊代码后,我得到了以下选择场景

选择场景

功能是正确的。剩下的就是为 c10c11 添加头像。

使用 neogeo 调试器,我找到了第一个角色(c03)的头像代码

063 0C80 B102 CB06 0FFF

Neogeo 调试器
sprite_number=0x063, neogeo_videoram[0x8463]=0x0C80, neogeo_videoram[0x8263]=0xB102, 
neogeo_videoram[0x18C0]=0xCB06, neogeo_videoram[0x8063]=0x0FFF

通过反复重播 1 的录制输入,我反向调试直到找到 mainrom 如何绘制 c03 的头像。

657FFE0 014206: 7000                  moveq    $0, D0 D0=0000FFFF->00000000
657FFE4 014208: 101C                  move.b   (A4)+, D0 D0=00000000->00000003 
                                                      A4=000142CC mainrom[0x142CC]=0x03
657FFEC 01420A: E748                  lsl.w    3, D0 D0=00000003->00000018 A4=000142CD
657FFF8 01420C: 337C 0013 0064        move.w   $13, ($64,A1) D0=00000018 
                                                             A1=00105DE0 mainram[0x5E44]=0x0013
6580008 014212: 3373 0000 0066        move.w   (A3,D0.w), ($66,A1) D0=00000018 
                                      A1=00105DE0 A3=0001423C 
                                      mainram[0x5E46]=mainrom[0x14254]=0x0070
658001E 014218: 3373 0002 004E        move.w   (A3,D0.w,$2), ($4E,A1) 
                                                mainram[0x5E2E]=mainrom[0x14256]=0x0029
6580034 01421E: 3373 0004 0050        move.w   (A3,D0.w,$4), ($50,A1) 
                                                mainram[0x5E30]=mainrom[0x14258]=0x00AE
…
6587C92 00344E: 3D6E 004E 005C        move.w   ($4E,A6), ($5C,A6) A6=00105DE0 
                                                mainram[0x5E3C]=mainram[0x5E2E]=0x0029
6587CA6 003454: 3D6E 0050 005E        move.w   ($50,A6), ($5E,A6) A6=00105DE0 
                                                mainram[0x5E3E]=mainram[0x5E30]=0x00AE
…
6587CFC 003470: 202E 0064             move.l   ($64,A6), 
                                                D0 D0=mainram[0x5E44]=0x00130070 A6=00105DE0
…
6587D98 0036AC: 302E 0064             move.w   ($64,A6), D0 D0=00130070->00130013 
                                                A6=00105DE0 mainram[0x5E44]=0x0013
6587DA4 0036B0: D040                  add.w    D0, D0 D0=00130013->00130026
6587DA8 0036B2: D040                  add.w    D0, D0 D0=00130026->0013004C
6587DAC 0036B4: 43F9 0022 0280        lea      $220280.l, A1
6587DB8 0036BA: 2271 0000             movea.l  (A1,D0.w), A1 D0=0013004C 
                                               A1=mainrom[0x1202CC]=002AC948
6587DCA 0036BE: 302E 0066             move.w   ($66,A6), D0 A6=00105DE0 mainram[0x5E46]=0x0070
6587DD6 0036C2: D040                  add.w    D0, D0 D0=00130070->001300E0
6587DDA 0036C4: D040                  add.w    D0, D0 D0=001300E0->001301C0
6587DDE 0036C6: 2D71 0000 006C        move.l   (A1,D0.w), ($6C,A6) D0=001301C0 
                                                A1=002AC948 A6=00105DE0 
                                                mainram[0x5E4C]=mainrom[0x1ACB08]=0x002AE40A
…
6587E0C 0034A0: 206E 006C             movea.l  ($6C,A6), A0 A0=mainram[0x5E4C]=0x002AE40A 
                                                A6=00105DE0
…
6587E36 0034D6: 1C18                  move.b   (A0)+, D6 A0=002AE40A->002AE40B
…
6587E4C 0034DC: 1A18                  move.b   (A0)+, D5 A0=002AE40B->002AE40C
…
6587E64 0034E6: 3D58 0074             move.w   (A0)+, ($74,A6) A0=002AE40C->002AE40E
…
6587EAE 0036CE: 5448                  addq.w   2, A0 A0=0002AE40E->002AE410
…
6588130 0035F6: 3030 3000             move.w   (A0,D3.w), D0 D0=00101040->00103958 
                                               D3=20200000 A0=002AE410 mainrom[0x1AE410]=0x3958
658813E 0035FA: 0280 0000 7FFF        andi.l   $7FFF, D0  D0=0010395A->0000395A
658814C 003600: 3740 0018             move.w   D0, ($18,A3) D0=00003958 A3=00101040 
                                                    mainram[0x1058]=0x3958
…
65881E6 00362C: 376E 005C 0010        move.w   ($5C,A6), ($10,A3) A3=00101040 
                                                A6=00105DE0 
                                                mainram[0x1050]=mainram[0x5E3C]=0x0029
65881FA 003632: 376E 005E 0012        move.w   ($5E,A6), ($12,A3) A3=00101040 
                                                A6=00105DE0 mainram[0x105E]=
                                                mainram[0x5E3E]=0x00AE
…
659545E 003318: 382E 0018             move.w   ($18,A6), D4 D4=FFFF0003->FFFF3958 
                                                         A6=00101040 mainram[0x1058]=0x3958
659546A 00331C: 4EB9 0000 4120        jsr      $4120.l
659547E 004120: 0284 0000 FFFF        andi.l   $FFFF, D4 D4=FFFF3958->00003958
659548C 004126: D884                  add.l    D4, D4 D4=00003958->000072B0
6595494 004128: D884                  add.l    D4, D4 D4=000072B0->0000E560
659549C 00412A: 43F9 0007 2000        lea      $72000.l, A1 A1=00072000
65954A8 004130: 2AB1 4800             move.l   (A1,D4.l), (A5) D4=0000E560 
                                      A5=00108000 mainram[0x8000]=mainrom[0x80560]=0x410BB296
65954C2 004134: 3015                  move.w   (A5), D0 D0=00100000->0010410B 
                                                mainram[0x8000]=0x410BB296
65954CA 004136: 0255 0007             andi.w   $7, (A5) D0=0010410B 
                                      A5=00108000 mainram[0x8000]=0x410BB296->0003B296
65954DA 00413A: 2255                  movea.l  (A5), A1 A1=mainram[0x8000]=0x0003B296 
                                                A5=00108000
65954E6 00413C: 4295                  clr.l    (A5) mainram[0x8000]=0x00000000
65954FA 00413E: D3C9                  adda.l   A1, A1 A1=0003B296->0007652C
6595502 004140: D3FC 0008 2004        adda.l   $82004, A1 A1=0007652C->000F8530
…
65980F6 003D82: D259                  add.w    (A1)+, D1 D1=00000000->0000ED10 
                                                A1=000F8530->000F8532 mainrom[0xF8530]=0xED10
65980FE 003D84: B541                  eor.w    D2, D1 D1=0000ED10 D2=00400000
6598102 003D86: C26D 8B7E             and.w    ($8B7E,A5), D1 D1=0000ED10 
                                                A5=00108000 mainram[B7E]=0xFFFF
659810E 003D8A: 3419                  move.w   (A1)+, D2 D2=00400000->0040CB06 
                                                A1=000F8532 mainrom[0xF8532]=0xCB06
6598116 003D8C: 1A3B 50E4             move.b   (PC,D5.w,-$1C), D5 D5=00000001->00000070 
                                                mainrom[0x3D73]=0x70
6598124 003D90: 3943 FFFE             move.w   D3, ($FFFE,A4) D3=000018C0
6598130 003D94: 4EFB 5002             jmp      (PC,D5.w,$2)
659813E 003E08: 3942 0000             move.w   D2, ($0,A4) D2=0040CB06 
                                               neogeo_videoram[0x18C0]=0xCB06 
                                               code_2=Neogeo.neogeo_videoram[sprite_number<<6]
…
6598262 003394: 302E 0012             move.w   ($12,A6), D0 D0=00000000->000000AE 
                                                         A6=00101040 mainram[0x1052]=0x00AE
659826E 003398: 9041                  sub.w    D1, D0 D0=000000AE->0000009E D1=00000010
6598272 00339A: 4440                  neg.w    D0 D0=0000009E->0000FF62
6598276 00339C: EF48                  lsl.w    7, D0 D0=0000FF62->0000B100
659828A 00339E: 806E 0008             or.w     ($8,A6), D0 D0=0000B100->0000B102 
                                                        A6=00101040 mainram[0x1048]=0x0002
6598296 0033A2: 36C0                  move.w   D0, (A3)+ D0=0000B102 A3=001007C4 
                                               mainram[0x7C4]=0xB102
…
6598320 0033E8: 302E 0010             move.w   ($10,A6), D0 A6=00101040 
                                               D0=mainram[0x1050]=0x0029
659832C 0033EC: 9041                  sub.w    D1, D0 D0=00000029->00000019 D1=00000010
6598330 0033EE: EF48                  lsl.w    7, D0 D0=00000019->00000C80
6598344 0033F0: 3880                  move.w   D0, (A4) D0=00000C80 A4=003C0002 neogeo_videoram
                                      [0x8463]=0x0C80 x_2=Neogeo.neogeo_videoram[0x8400|
                                      sprite_number]
…
65A72B6 0031FE: 3898                  move.w   (A0)+, (A4) A0=001007C4 A4=003C0002 
                                      neogeo_videoram[0x8263]=mainram[0x7C4]=0xB102 
                                      y_control=Neogeo.neogeo_videoram[0x8200|sprite_number]

我发现以下标记的代码决定了 c00-c11 头像的坐标和精灵代码

主 CPU ROM

15 个角色的原始坐标

x coordinate:
29 4B 6D 8F B1 D3 F5 117
 39 5B 7D 9F C1 E3 105
y coordinate:
AE AA A7 A5 A5 A7 AA AE
 D0 CC C9 C8 C9 CC D0

我将坐标扩展到 17 个角色

x coordinate:
 29 4B 6D 8F B1 D3 F5 117
17 39 5B 7D 9F C1 E3 105 127
y coordinate:
 AE AA A7 A5 A5 A7 AA AE
D5 D0 CC C9 C8 C9 CC D0 D5

因此,我得到了以下作弊代码

[17 select 2]
ON=1387D,CB;141E9,CB;141ED,10;142BE,01;142BF,27;142C1,D5;142C7,17;142C9,
D5;142CB,11;142DB,10;1471B,11;14739,10;14747,CB

应用上述作弊代码后,我得到了以下选择场景

选择场景

通过 Neogeo 调试,我找到了 17 个角色的以下 sprite_gfx 偏移量

角色 ID mainrom 精灵代码 sprite_gfx 偏移量
07 6F 1CB0200
03 70 1CB0600
00 71 1CB0A00
05 72 1CB0E00
0B 73 1CB1200
08 74 1CB1600
01 75 1CB1A00
04 76 1CB1E00
09 77 1CB2200
02 78 1CB2600
06 79 1CB2A00
0C 7A 1CB2E00
0E 7B 1CB3200
0F 7C 1CB3600
0D 7D 1CB3A00
11 7E 1CDC600
10 ? 1F5A700

现在我将根据已知的 sprite_gfx 偏移量计算 c10mainrom 精灵代码。

已知的 c03 计算

0x03*8=0x18
0x1423C+0x18=0x14254
mainrom[0x14254]=0x0070
0x70*4=0x1C0
0x2AC948+0x1C0=0x2ACB08
mainrom[0x1ACB08]=0x002AE40A
0x1AE40A+0x06=0x1AE410
mainrom[0x1AE410]=0x3958
0x3958*4=0xE560
0x72000+0xE560=0x80560
mainrom[0x80560]=0x410BB296
0x410BB296&0x7FFFF=0x3B296
0x3B296*2=0x7652C
0x7652C+0x82004=0xF8530
0xF8530+2=0xF8532
mainrom[0xF8532]=0xCB06
CB06->1CB0600

c10 的计算

1F5A700->F5A7
The reasonable mainrom address is 0xA62B6.
mainrom[0xA62B6]=0xF5A7
0xA62B6-2=0xA62B4
0xA62B4-0x82004=0x242B0
0x242B0/2=0x12158
0x12158+(0x410BB296-0x3B296)=0x41092158
The mainrom address is 0x77198.
mainrom[0x77198]=0x41092158
0x77198-0x72000=0x5198
0x5198/4=0x1466
The reasonable mainrom address is 0x1B21D8
mainrom[0x1B21D8]=0x1466
0x1B21D8-6=0x1B21D2
The mainrom address is 0x1AD530.
mainrom[0x1AD530]=0x002B21D2
0x2AD530-0x2AC948=0xBE8
0xBE8/4=0x2FA
0x10*8=0x80
0x1423C+0x80=0x142BC

我将把 mainrom[0x142BC] 改为 0x02FA。因此,我得到了以下作弊代码

[17 select]
ON=1387D,CB;141E9,CB;141ED,10;142BC,02;142BD,FA;142BE,01;142BF,27;142C1,
D5;142C7,17;142C9,D5;142CB,11;142DB,10;1471B,11;14739,10;14747,CB

修改后的 ROM 如标题图像所示。

结论

我已完成了 MAME.NET。它能够弄清楚街机游戏是如何工作的。您可以参考 MAME 源代码来支持任何其他游戏。通过从 C 到 C# 的翻译,不安全的代码很少,代码的可读性也大大提高。我保留了 MAME 的主要架构。程序员可以参考 MAME.NET 源代码,并使用 C# 来模拟更多街机游戏系统。

历史

  • 2019年2月1日:完成 MAME.NET,支持 792 款游戏(构建 20190201)
  • 2019年6月4日:添加了 M72 和 M92 主板,支持 826 款游戏(构建 20190604)
  • 2020年5月19日:添加了 Taito B 主板,支持 830 款游戏(构建 20200519)
  • 2020年12月3日:添加了 Konami 68000 主板,支持 882 款游戏(构建 20201203)
  • 2021年6月16日:添加了 Capcom 主板,支持 888 款游戏(构建 20210616)
  • 2021年8月5日:支持 890 款游戏(构建 20210805)
  • 2022年2月27日:添加 ROM 修改:吞食天地,卡萨尔一直坐着
  • 2023年2月8日:添加了 Taito 主板,支持 912 款游戏(构建 20230208)
  • 2023年4月24日:添加了鼠标支持,支持 918 款游戏(构建 20230424)
  • 2023年8月14日:添加了 11 款 Capcom 游戏,支持 929 款游戏(构建 20230814)
  • 2024年2月19日:添加了 5 款 Tehkan 游戏,支持 934 款游戏(构建 20240219)
  • 2024年6月20日,添加了 6 款 Data East 游戏,支持 940 款游戏(构建 20240620)

参考文献

  1. MAME - Multiple Arcade Machine Emulator - https://github.com/mamedev
  2. MSDN - https://msdn.microsoft.com
  3. BizHawk M68000 和 Z80 代码 - https://github.com/TASEmulators/BizHawk/tree/master/src/BizHawk.Emulation.Cores/CPUs
  4. VCMAME 详情 by Bryan McPhail - https://codeproject.org.cn/Articles/4923/VCMAME-Multiple-Arcade-Machine-Emulator-for-Visual
  5. MAME 和 MAMEUI Visual C 项目文件 - http://www.mikesarcade.com/arcade/vcmame.html
  6. CPS1.NET - https://codeproject.org.cn/Articles/998595/CPS1-NET-A-Csharp-Based-CPS1-MAME-Emulator
© . All rights reserved.