fc nes中 超级玛丽的一个bug

作者:懒狗
链接:https://www.zhihu.com/question/278030500/answer/405485072
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 

最近,创下《超级马里奥兄弟》1代最快通关记录的HappyLee在B站直播的时候,展示了一个非常有趣的bug。这个bug正常情况下只能在6-1中出现(有待考究)。写这个回答的时候随便模拟了一下情形。

【这里有个视频,没法copy,去看原链接吧】

可以看到,在恰当的时机里,借助方块顶上面的刺猬时,刺猬竟然变成了啪嗒龟!而且还有一绿一红(红色那个被顶出屏幕外面了)!这里的原理是什么呢?


以下汇编代码来自:https://gist.github.com/1wErt3r/4048722

FC/NES里使用的6502 CPU有A、X、Y、P、S寄存器以及各种Flag(标记位),这里主要讨论A寄存器。

首先我们看游戏中踩到敌人并击晕的代码:

fc nes中 超级玛丽的一个bug

这里只摘取有用片段

一句话解释就是,当A里存着的值大于9而小于17,且不等于13时,会执行Demote处代码。而Demote的作用是,将其他类型的诺库龟(比如飞行诺库龟)转换为看起来正常的诺库龟(实际上红色的诺库龟已经不具备躲悬崖的能力了)。具体的做法是判断敌人ID的奇偶

下面是敌人的ID表,同样只列出有用的部分。

fc nes中 超级玛丽的一个bug

注意到9号那个异类,正常游戏里不会出现

假设是15号敌人,因为是奇数,所以会转换为1号敌人。以此类推。

 

其次,回想一下,我们攻击敌人的时候,是不是会冒出一个分数?这个bug得以实现,还真得感谢这个功能。

fc nes中 超级玛丽的一个bug

这段代码执行完毕后会跳回到调用处

图中11539行代码会将敌人的相对坐标存进A寄存器。那么什么又是相对坐标呢?就是敌人距离屏幕左侧的距离。漂浮的分数的横坐标与敌人的横坐标对齐,所以这个操作很正常。

但是,当年的程序员并没有注意到,他们代码的逻辑出现了漏洞。

fc nes中 超级玛丽的一个bug

敌人和背景方块碰撞时的代码,下面直接连着击晕代码

我们可以看到,在代码运行到12453行之后,并没有对寄存器A进行改写,所以寄存器A存着的是敌人的相对坐标,而代码就直接运行到了踩到敌人并击晕的子程序!只要相对坐标控制好,我们可以改写敌人的ID!而改写的条件,就是大于9而小于17,且不等于13!还有一个条件,原来的敌人不能是板栗仔(Goomba),否则这段代码不会被执行。换句话说,其他能在砖块上行走的敌人,都可以用这个bug转换为诺库龟!

 

弄明白原理之后,我稍微改了一下地图,录了一个TAS,当然,游戏内的其他地方并没有修改。

【这里也有一个视频】

整个视频里花俏的东西有点多,不过关键在这里:

fc nes中 超级玛丽的一个bug

图片中白线和文字表示敌人相对坐标

在马里奥顶到砖块的前一刻,敌人的相对坐标是15,按照前面的ID表和规则,这一次能将敌人转换为红色啪嗒龟。后来的踩龟壳和飞踢,也证明了转换成功了(虽然换为刺猬会更有说服力)。

视频里显示的白线和坐标是我自己写的外挂脚本,同样也没有影响游戏。

之前还录过转换为绿色诺库龟的版本,但是一不小心覆盖了……


分析时使用的工具:

Mesen(反汇编比较舒服)

FCEUX(录制TAS,脚本辅助分析)

SMB Utility(修改ROM,以及正确的敌人列表)