几年前,我和几个朋友坐在Google NYC内部的一家咖啡馆。 我们谈论的是90年代错过的事情,我说了我小时候喜欢16位游戏的程度。 我的一个朋友碰巧有两个他不在车库里使用的SNES,并说我可以拥有其中一个。 几天后,我发现桌上装有Zelda游戏卡带的SNES。

我没有监视器,所以回到家时,我尝试将其连接到室友的投影仪上。 游戏机已打开,但未显示任何内容。 后来,我发现这很可能是由于灰尘或接触不当而导致无法正确读取游戏卡带引起的。 我试着吹动墨盒,但这无济于事,所以它在我皇后区的客厅里坐了大约一年,当我搬到布鲁克林时,它伴随着我,在我的厨房里发现了一个隐藏在柜子上方的地方。收集更多的灰尘。
有一天,我的朋友Phoebe在递归中心上作了有关硬件黑客的演讲。 她提到了有关硬件漏洞的资源和有关安全性的搜索引擎。 她还提到了Bus Pirate,以及如何将其用于侦听电路板上的串行通信。 我想从使用它的东西中得到一些有趣的数据。
在对从设备中转储数据感兴趣之后,SNES发现了一种可能的新用途。 我的朋友Cameron找到了它的原理图,并提到它似乎有很多并行总线。 并行接口同时传送多个位,每个位都有一根专用的线。 串行接口一次传送一位。 这些位以一系列脉冲的形式传送。 由于SNES使用并行通信,因此它可能不是Bus Pirate探索的最佳选择。
尽管如此,我想进一步了解SNES的内幕,所以我将其带到了Recurse中心并开始将其拆散。 需要一个特殊的螺丝起子来打开游戏盒以及游戏机本身。 我在网上找到了一段视频,视频中介绍了如何通过熔化塑料笔的笔尖,然后再次按下螺钉将其成型为螺丝起子的形状,从而制造出临时螺丝起子,但决定购买螺丝起子会更容易。
再次插入SNES并将其打开后,出现黑屏。 另一个朋友艾利(Avery)决定用毛巾擦拭墨盒读取器。 当我们插入电源并将SNES连接到投影仪时,这次它可以工作了。 几天后,螺丝刀收到了邮件,我很高兴继续分解SNES。 有一个弹簧按住着墨盒释放按钮。 我卸下了游戏墨盒阅读器和声卡。 盒式读卡器没有焊接,也没有拧入。所以我将其取出,然后放回去。
在游戏运行时,我们开始使用Saleae逻辑分析仪来嗅探并行总线。 为此,我们将探针夹直接钩在Zelda ROM芯片引线上。

在读取了ROM盒带和SNES之后,我们了解到不必为了转储ROM而将盒带连接到控制台。 通过研究SNES盒式ROM的引脚排列,我们了解了可以为ROM供电并与之通信,就像我们是SNES一样。 如果使用地址和输出使能引脚将并行通信发送到ROM,则ROM将通过数据引脚输出数据。 因此,我们将Arduino连接到ROM,并编写了一些C代码,该代码按地址迭代所有可能的存储位置,以请求数据。 然后,将Saleae逻辑分析仪连接到8个数字输出引脚,并收集ROM转储。 我们有效地将输出使能引脚用作时钟,因此我们不需要单独的时钟信号。

白色尖峰代表高信号,二进制1值。 紫色通道是时钟,用于确定何时将样本视为有效。
有一些警告,当然,这并不是那么简单。
首先,需要21个引脚来请求所有地址(20个地址引脚和1个输出使能引脚),但是Arduino只有20个引脚可以用作数字输出。 Cameron发现周围有一个MC14015B芯片,这是一个串行到并行转换器,因此它使我们能够使用Arduino上的两个引脚与Zelda芯片上的8个引脚进行通信。 为此,我们将数据引脚写入8次,然后使用时钟引脚移位数据。 因为MC14015B有两个4位移位寄存器,并且我们想使用8个寄存器,所以我们使用了跳线使其用作8位寄存器。

其次,我们没有足够的测试夹来钩住每个ROM引脚。 我们需要连接到所有32个ROM引脚,而且我们只有8个左右的夹子,因此Cameron可以通过Zelda游戏盒PCB的背面将一束电线焊接到芯片上。

第三,Saleae逻辑分析仪一次只能从8个引脚获取数据,但是为了在正确的时间获取样本,我们需要9个引脚。 我们需要8个数字输出引脚和1个时钟引脚来告诉分析仪何时认为样品有效。 因此,我们获取了两个读数,更改了我们确定的范围,然后用Python脚本将它们缝合在一起。 面包板上的LED有助于调试。 我们添加了一些较长的闪烁,以便我们知道何时收集数据。

这不是第一次转储SNES游戏卡带。 实际上,几乎可以在线找到任何SNES游戏二进制文件。 这些二进制文件用于在模拟器上玩游戏。 为了验证我们是否转储了正确的数据,我下载了其中一个二进制文件。 将我们的ROM转储与下载的ROM转储进行比较,我们发现了一些噪音。 大约3%的价值观不同意。 通过查看图像输出,您可以看到一个示例。 您会注意到从我们的rom转储生成的图像有一个额外的像素。

要尝试排除可能导致该像素的故障,了解Saleae逻辑分析仪设置很重要。

左: 我们将红线连接到哪里的方法 。 右上方: Saleae逻辑分析仪设置 。 右下:逻辑分析仪采样率。 MS / s是每秒1百万(百万)样本。
关于我们的转储为何嘈杂的一种理论是,我们采样的频率不够高。 我们使用奈奎斯特定律确定噪声可能与我们的采样率无关。 在Nyquist之后,足够的采样率大于每秒2B个采样。 在1MS / s的速度下,我们应该可靠地捕获高达500kHz的信号,这是在我们最大总线速度〜1 /(10us)= 100kHz(其中10us是delay_time)时的安全裕度。
为什么我们的初始ROM转储会产生噪声的另一种理论是,我们收集的数据太接近输出使能(/ OE)信号的下降沿。 因此,我们认为更改Arduino代码以在数据变低后的微秒内发送一个额外的时钟引脚一个脉冲可以帮助消除噪声。 我们没有检验这个理论。 “数据在时钟下降沿有效”是指,每当时钟位(或在我们的情况下为输出使能)从高电平变为低电平时,Saleae输出一个样本。 我们将分析仪设置更改为在上升沿而不是下降沿采样数据,并且噪声消失了。
要从二进制数据中提取精灵,我们需要了解精灵如何存储在SNES游戏卡带ROM中。 将每个像素的RGB值一起存储在便携式像素图文件中时,将交错使用SNES子画面,并使用调色板存储颜色。 因此,我们将.pbm文件的标头设置为仅呈现一种颜色(黑色或白色),然后将宽度设置为8像素,即SNES磁贴的宽度。 Cameron写了一行bash,将数据另存为单独的图像文件,因为它不会作为一个文件打开。
之后,我们可以找出游戏中使用的一些精灵!

我们还设法发现了在游戏开始的头几秒钟中使用的开头徽标精灵,并将它们粘贴在一起。


感谢Cameron在这方面与我配对,并成为我的黑客伙伴。 您的Python,Bash和Linux技能令人印象深刻,在此您从中学到了很多,我非常感激!
感谢艾利(Avery)非常出色,分享您的硬件专业知识和游戏控制台的智慧!
感谢Ben,Steven和Cameron审阅此博客文章并提供宝贵的反馈!
感谢Phoebe的精彩演讲激发了这一探索!
感谢递归中心提供的空间和工具来实现这一目标!