一起来做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/