一起来做NES开发(2)-反汇编

本文深入探讨如何通过反汇编技术解析NES游戏的机器码,以研究游戏内部逻辑。从理解NES CPU类型、寻找Mapper=0游戏、判断机器码首地址、分离机器码直至反汇编成汇编码,逐步指导读者掌握反汇编方法。通过实例分析,揭示简单游戏的反汇编过程及技巧,帮助开发者深入理解游戏开发原理。
摘要由CSDN通过智能技术生成
 

一起来做NES开发(2)-反汇编

 

 

维京猎人

 

摘要

在刚接触NES时,大家总是想了解:别人是怎样写NES游戏的。那么能不能将NES文件译成代码文件来研究呢?答案是肯定的,方法就是反汇编。但复杂的游戏是难以完整的反汇编。我们从简单的说起。反汇编的3个要素:CPU型号、机器码段的开始和结束在NES文件上的地址、机器码段运行时载入NES内部的首地址。本文主要述说NES的反汇编工具的用法。

 

 

正文

 

NES的CPU类型 :6502 (NES的CPU真正型号是6527或2A03,这都是以6502为基础芯片的,使用反汇编工具时选CPU:6502就OK)

 

“假如有一段数据,你确定它是机器码,也知道它将会在哪个地址段上运行,那就可以反汇编了。”

 

说起反汇编,那可是非常难的。这不是指从机器码到汇编码的过程,这只要查表就行,难是难在:

(1)  要将机器码从NES游戏文件(或程序文件)上分离出来,并能确定机器码段的数量和各段的头尾位置。

(2)  要确定各段机器码在运行时的首地址。

 

 

这听起来太复杂了,吓退一帮初学者。对于复杂的游戏,我也是无能为力。

要是只是研究简单的游戏,或者局部的进行反汇编,我这里倒是有一套简单的办法。

 

如何理解我所说的“简单的游戏”?

这个就是指NES游戏的Mapper=0

你不禁要问Mapper是什么,我下面的内容会解答的。

 

 

 

***************************************************

**             我们来操作一下吧                  **

***************************************************

以下内容,地址全用16进制表示。我习惯用$xxxx表示16进制。

 

 

一、如何找到Mapper=0的NES游戏?

你自然有不少的NES文件,同时也知道上哪去下载这些文件,你可以多下载一些你觉得简单的游戏。我下面介绍查看mapper值的方法。

我已经找到几个Mapper=0的游戏:超级玛丽、坦克大战

方法有两个

方法(1) VirtuaNES

用VirtuaNES 打开NES文件,点菜单“文件-ROM信息”就可以查看mapper值了。

方法(2) NesHeader

用网友“疾风の迅雷”写的一个软件NesHeader.exe,可以批量读取整个文件夹上所有NES文件的ROM信息。

 

**

找到Mapper0的NES游戏之后,我们来确定机器码的运行首地址。

Mapper0的nes只有一段机器码段,这就好办了。其首地址有可能是

   $8000  或  $C000

 

先不要问我为什么有两个可能性,我先来教你判断。

 

二、怎样判断Mapper0的NES游戏的机器码的首地址。

第一步:我提供两个办法。

先看NES文件的大小,

NES文件

首地址

例子

24K

$8000或$C000

坦克大战

40K

$8000

超级玛丽

 

另一个办法:用上面提到的NesHeader.exe,查看PROM大小,

PROM大小

首地址

例子

1x16K

$8000或$C000

坦克大战

2x16K

$8000

超级玛丽

 

第二步:如果nes大小=24K则要查看游戏程序的运行地址区域。

用Fcdebug打开ROM,点菜单DEBUG,打开“指令显示/控制”窗口。按“暂停”,查看(运行)地址。(这时看到的是正在运行任一条指令)

地址

首地址

在$8000到$BFFF之间

$8000

在$C000到$FFFF之间

$C000

 

 

本办法是针对Mapper0的NES而设计的,对于别的Mapper值则完全不适用。

 

钻牛角的人要跑来问“别的Mapper是如何找到首地址?”

我在文章的后面解答一下吧。

 

**

接下来要将机器码从NES文件上分离出来。

你要知道,NES文件是由文件头(Head)、程序ROM(即PROM)、图形ROM(即VROM)组成。在ROM里面,连接的顺也是Head-PROM-VROM。其中Head固定占16字节;PROM占[n x 16K],n是一个倍数;VROM占[m x 8K],m是一个倍数。

朋友们看出来了,这个PROM的大小和VROM的大小在ROM信息中出现过。

PROM一般是16K为1页(1 bank),也有8K,4K为单位的。因不同的Mapper值而异。(NES文件中统统以16K为单位记录)

我们专门说Mapper0,这个号是不切页的,所以可以看成只有1页,也就是说:

注:机器码首地址,指机器码运行时在寄存器中的首地址,不是NES文件上的地址。

NES大小

PROM大小

机器码首地址

机器码末地址

机器码长度

40K

32K

$8000

$FFFF

32K

24K

16k

$8000

$BFFF

16K

16K

$C000

$FFFF

16K

 

在上面的述中揭示了一些东西。要是看不明白也没关系,下面可以照着做。

 

 

三、如何才能将机器码从NES文件上分离出来?

我提供两个办法。上面用到的地址,我都算准了。

 

方法(1)DUMP法:

用FCdebug(NES_debug)运行ROM,点菜单DEBUG,打开“指令显示/控制”窗口。按“暂停”,在窗口的最底行,找到“内存”,在边上的两个编辑框里,填入首地址和末地址,建议末地址统一填FFFF,其中$号省略。然后按“DUMP下来”,保存成mem文件。

 

FCdebug是VirtuaNES的一个改版,加入了debug功能。

 

方法(2)HEX编辑法:

用HEX类编辑软件,打开ROM。例如Hex Workshop。

删除前16个字节,然后拉滚动条:

原ROM文件大小

拉到地址(分界点)

说明

40K

8000

这个位置就是分界点,上面是PROM,下面是VROM

24k

4000

将这个地址以下的部分(包括这个分界点)删除或剪切走。留下的部分另存为mem文件。

 

 

**

终于到了反汇编软件上场了。

有人会问:“这个mem是什么文件?”

这个是内存文件,其实就是机器码,里面没有别的东西了。这个后缀名不重要,因为都没有关联软件。你叫他bin文件或别的都行。我只为统一叫法。

 

四、如何将mem文件显示成汇编代码呢?

我用这个

看图标,这是我用的“反汇编器”。它可以对多种CPU进行反汇编。

 

(1)打开它,先要选CPU,在窗口右上角选6502。

(2)用这个反汇编软件,打开mem文件。

(3)然后点选项“BIN文件”,在开始地址,填上“首地址”,

(4)确定提勾“立即反编译”,点“确定”就OK

(5)保存反汇编的asm文档,结束。

 

**

汇编代码是反出来了。可是问题来了:“从何看起?”

针对NES的结构,机器码是有一个入口的,这个入口的地址写在一个固定的地方。我们叫“指针”。共有3个指针,都放在一起,分别指向NMI、RESET、IRQ|BRK。其中RESET就是主程序的入口,NMI是中断的入口,IRQ|BRK是另一种中断入口。

 

 

五、如何找到程序的入口?

将asm文件(用记事本)打开,滚动条向下拉,拉到底。看到FFFA到FFFF的数据,抄下来。

FFFA

FFFB

FFFC

FFFD

FFFE

FFFF

a

b

c

d

e

f

 

记a,b 是一组指针,实际地址为ba,即a为低位,b为高位。如此类推得3组指针。

 

NMI = ba

RESET = dc

IRQ|BRK = fe

 

例如 这是CC65产生的典型的ROM

FFFA

FFFB

FFFC

FFFD

FFFE

FFFF

35

80

00

80

5C

80

 

NMI = $8035

RESET = $8000

IRQ|BRK = $805C

 

 

六、注意事项

反汇编的结果不是全都是代码,其中有部分可能会是数据,也有可能整页都是图型(CHR)数据。反汇编器总是优先将能够反成代码的译成代码。于是你会发现在DB旁边出现没有作用的代码,有时发现代码去读一个指令,其实是在读指针或数据。

另外一些软件生成的ROM会加入一些没机会运行的代码。这可能是优化不完全。

 

 

七、局部反汇编,只做第三步和第四步就行。其中第三步用dump法。具体,多接触就会了解。

 

 

 

疑难解答:

Mapper是什么?

    Mapper是一个编号,用于区分不同电路结构的卡带。NES模拟器拿一个ROM当作是一个卡带来模拟,那么卡带的电路也要区分才能模拟的。卡带不是标准统一的,当时不同的NES游戏开发商都开发出自己独有的保密的卡带;还发展出多个不同的升级产品,可以令游戏性能更理想;同时对游戏程序有保密作用。

 

为什么Mapper0的ROM机器码首地址有可能是$8000或$C000?

Mapper0就是表示卡带电路没有任何特别,卡带的寄存器是直接接到CPU总线上的。也就是有可能

(1)       16K的寄存器接到$8000-$BFFF的地址上并镜像到$C000-FFFF。

(2)       32K的寄存器接到$8000-$FFFF的地址上。

在(1)情况下,$8000-$BFFF和$C000-FFFF的机器码是完全一样的。主要是看程序的作者,将代码(基于).org $8000还是.org $C000进行编译的。

我本来想在“判断首地址”的方法用程序入口的办法,但这个不准确,因为在(1)情况下我可以恶作剧的将程序入口改成对应镜像的位置,程序一样是正常运行的。

 

Mapper0以外的ROM是如何找到首地址?

只有Mapper0不切页,换句话说,Mapper0是静态将寄存器接入总线。其余的Mapper都是动态的将寄存器接入总线。也就是说,是通过程序控制接入某一块寄存器接入总线上某地址段,而原接入的块(页)断开。

答案就是:你要先知道最早是默认接入的寄存器页,将之按mapper0的方法反汇编。然后从程序入口,一行一行看代码,看调入了哪一页,然后计算这一页的位置,将它分离出来。再反汇编。如此直到掌握全部页的动向。

嘿嘿,是不是受不了。

 

 

下一集再见。

以上软件都可以在CSDN找到,或到我的网盘http://fogota.ys168.com/

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值