SNES模拟器离绝对完美只有几个像素


我们非常接近创建一个模拟器,可以完美地重建真实硬件和SNES软件的所有功能。

在过去的15年中,作为bsnes仿真器的编码器我试图完善Super Nintendo仿真,但现在我们面临的最后一个问题是:SNES视频处理器的时钟周期的准确计时。为了达到仿真准确性的最后阶段,需要整个社区的帮助,我希望得到您的支持。但首先,我将告诉您我们已经取得的成就。

当前状态


如今,使用SNES模拟的情况非常好。除了抗拒模拟的异常外围设备(例如,在日本用于日本的赛马博彩的带有光传感器高尔夫俱乐部自行车模拟器和拨号调制解调器)之外,所有官方授权的SNES游戏都可以完全玩,而且没有游戏存在明显问题。 SNES仿真变得如此精确,以至于我什至不得不将仿真器分为两个版本:higan和bsnes,它们追求绝对的准确性和与硬件文档的一致性,而bsnes追求速度,广泛的功能和易用性。



最近,在SNES仿真领域,已经收到了许多有趣的成就,包括:


… 以及更多!

那么,完成了吗?每个人都工作得很好吗,再见,并感谢你的鱼吗?好吧,不完全是。

今天,我们已经在几乎所有SNES组件的节拍水平上实现了准确性。唯一的例外是用于生成传输到屏幕的视频帧的PPU(图像处理单元,图像处理模块)。我们大多数人都知道PPU的工作原理,但是对于某些功能,我们必须使用猜测功能,这会导致精度不完善。

从总体上看,剩下的问题很小。如果您不为对艺术的热爱而追求绝对完美的仿真理想,那么我无法说服您进一步改进PPU仿真的必要性。就像在任何领域一样,我们越接近理想,回报就越低。

但我可以说,为什么这是重要的:这是我一生的工作,我不想让我说,我有这么接近完成而不采取的最后一步。我正在衰老,我不是永恒的。我希望解决难题的最后一部分,以便在退休后能够确定SNES的遗产是可靠的,并且可以通过仿真完全保留下来。我想说问题已经解决

如果您仍然感兴趣,请继续阅读以了解我提供的问题和解决方案的背景。

建模SNES体系结构


让我们首先列出组成SNES的组件:


超级NES系统图。

箭头指示各种SNES处理器可以彼此交换数据的方向,虚线指示与存储芯片的连接。

现在对我们来说最重要的是要注意,视频和声音的输出是直接从PPU和DSP传输的。这意味着它们充当“黑匣子”,我们看不到它们内部正在发生什么。以后它将对我们变得重要。

正确性


想象一下,我们模拟CPU命令“乘”,它将两个寄存器(变量)相乘,相乘,接收结果以及几个指示结果状态的标志(例如,overflow)。

我们可以编写一个程序,将0到255之间的任何可能值乘以一个乘数。然后,我们可以得出数值和标志相乘的结果。因此,我们得到两个表,包含65 536个元素。

通过分析这些表,我们可以准确地确定以某种方式设置CPU计算结果的方式和位置。然后,我们可以修改仿真器,以便在运行相同的测试时,可以同时获得完全相同的表。

现在,我们说CPU可以进行16位x 16位乘法。当测试每个可能的值时,将产生40亿个结果,这几乎不可能在合理的时间内进行测试。如果CPU具有32位x 32位的乘法,那么实际上将不可能在宇宙热死之前测试输入值的所有组合(至少在当前技术水平上)。

在这种情况下,我们将在测试中采取更具选择性的行动,并尝试确定何时可以准确更改标志,何时可能溢出结果等等。否则,我们将不得不运行永远不会结束的测试。

乘法是相当琐碎的操作,但是相同的原理可以扩展到整个反向工程过程,包括更复杂的操作,例如,在光束的水平返回路径中通过DMA(直接内存访问)进行数据传输。我们创建测试来确定在临界情况下会发生什么,然后检查我们的仿真行为是否与真实SNES的行为相同。

信号发生器和节拍


SNES有两个信号发生器(振荡器):一个以大约21 MHz的频率运行的晶体振荡器(它控制CPU和PPU模块),和一个以大约24 MHz的频率运行的陶瓷谐振器,它控制SMP和DSP。在盒式协处理器中,有时会使用21 MHz的晶体振荡器,有时会使用自己的信号发生器以其他频率工作。


用代码重新创建此Super Famicom电路板比听起来困难。

时钟是任何系统定时的基本元素,SNES旨在以一定的频率和时间间隔执行各种任务。

如果您想象一个100赫兹的时钟,这将是一个具有二进制输出的设备,该设备每秒切换到信号的高逻辑状态(例如+5 V),然后切换到信号的低状态(0 V或地)每秒100次。也就是说,输出电压每秒钟都会波动200次:增加100倍,并降低100倍降低时钟信号的前沿。

通常将一个时钟周期视为一个完整的过渡,即100 Hz周期每秒将生成100个时钟周期。一些系统需要在上升沿和下降沿之间进行区分,为此,我们将周期分成半个周期,以指示时钟信号的每个相位(高或低)。

精确仿真器最重要的任务是与在真实设备上完全相同的方式并在完全相同的时间完成任务。但是,如何执行任务不是很重要唯一重要的是仿真器在接收相同输入信号的同时,会在实际硬件上同时生成相同的输出信号。

时机


有时操作需要时间。以SNES CPU中的乘法为例。 SNES CPU无需暂停并等待乘法完成,而是在后台对CPU操作码的八个时钟周期一次计算一位的乘法结果。这有可能使代码在等待乘法完成的同时执行其他任务。

最有可能的是,任何商用软件都将等待这八个时钟周期,因为如果我们在准备就绪之前尝试读取结果,则会得到部分完成的结果。但是,在SNES仿真器立即给出正确的结果之前,无需等待这些额外的时钟周期。

当控制台迷开始在模拟器中创建和测试自写软件时,这种差异开始引起某些问题。该软件的一部分,例如,许多第一个Super Mario World ROM hacks ,只能在这些旧的仿真器上正确运行,而不能在真正的SNES硬件上正常运行。发生这种情况是因为开发它们时考虑了乘法结果的即时获取(从实际设备的角度来看是不可靠的)。

在改进仿真器的过程中,旧软件的兼容性被破坏,因此我们必须向新仿真器添加兼容性选项,以免丢失这些程序。是的,无论听起来多么超现实,但今天的模拟器必须模仿其他模拟器!

CPU中这种乘法延迟的便利性在于它是非常可预测的事实:在乘法操作请求之后,立即开始八个计算时钟周期。通过编写在每个周期之后读取结果的代码,我们能够验证SNES CPU是否使用Booth算法进行乘法运算

时钟同步


其他操作不容易建模,因为它们在后台异步执行。一种这样的情况是中央SNES处理器的DRAM更新。

在绘制每条光栅线期间,整个SNES CPU在某个阶段会在短时间内暂停其操作,同时会更新RAM芯片的内容。这是必要的,因为为了降低SNES的成本,动态(而非静态)RAM被用作CPU的主存储器。要保存动态RAM的内容,必须定期对其进行更新。


创建一个真正完美的仿真器不足以确保所有三千半SNES游戏的可玩性。还必须以完美的间歇精度实现系统各功能的仿真。

分析这些操作的确切时间的关键因素是可以使用水平和垂直PPU计数器。这些计数器执行递增操作,并在每次反向水平和垂直光束传播后重置。但是,它们的精度仅为SNES CPU信号发生器频率的四分之一。换句话说,水平计数器每四个时钟周期递增一次。

通过读取计数器值的几倍,我能够确定计数器与时钟周期的哪四分之一对齐。将这些知识与专门创建的功能相结合,可以朝用户指示的确切时钟周期数迈出一步,我就能够将SNES CPU与我所需的时钟周期的任何确切位置完美匹配。

由于要经过多个时钟周期的迭代遍历,因此我能够确定何时确实发生了某些操作(例如,更新DRAM,传输HDMA,中断轮询等)。之后,我可以在仿真中完全重新创建所有这些内容。

SMP芯片SNES控制台也有自己的计时器,并且还对该处理器执行了成功的逆向工程。我只能将整篇文章专门用于SMP TEST寄存器,这使程序员可以控制SMP分频器及其计时器,更不用说其他可怕的事情了。可以说这不是一个简单而快速的过程,但最终我们赢了。

我们收集协处理器



SuperFX芯片只是SNES仿真器可以处理的众多盒式协处理器之一。

我们还需要驯服各种游戏卡带中使用的一堆SNES协处理器。从单个通用CPU(例如SuperFXSA-1),数字信号处理器(例如DSP-1和Cx4)到解压加速器(例如S-DD1和SPC7110)或Sharp和Epson实时时钟,等等。

这意味着SNES仿真器必须处理SuperFX指令和像素缓存。采用SA-1内存总线冲突解决方案(允许SNES和SA-1 CPU同时使用相同的ROM和RAM芯片);带有集成固件DSP-1和Cx4;带有基于预测的算术编码器S-DD1和SPC7110;以及实时发生器中BCD(二进制编码的十进制)的奇数边界情况。但可以肯定地,通过使用上述用于确定正确性和时序的所有技术,我们设法学会了如何几乎完美地仿真所有这些芯片。

拆下芯片盖并从不同游戏中使用的数字信号处理器中删除固件需要花费大量的精力和数千美元。在一种情况下,允许NEC uPD772x仿真使用higan中的代码保存已故的Stephen Hawking的声音!

在另一种情况下,由于没有人发布过该架构的文档,因此我们需要对Hitachi HG51B架构的整个指令进行反向工程。在另一种情况下,事实证明,一款游戏(Hayazashi Nidan Morita Shougi 2)具有功能强大的32位ARM6 CPU,频率为21 MHz,可以加速日本将棋游戏!

仅仅保存所有的SNES协处理器,是一个长期的过程,充满了困难和惊喜。

数字信号处理


不应与DSP-1盒式协处理器混淆的Sony S-DSP(数字信号处理器)芯片会产生独特的SNES声音。在该芯片中,连接了8个采用4位ADPCM编码的音频通道,从而确保了创建16位立体声信号。

从表面上看,从上面的系统图向外看,DSP似乎是一个“黑匣子”:我们调整声音通道和混音器参数,然后芯片产生声音并传输到扬声器。

但是一个重要功能允许昵称为blargg的开发人员对该芯片执行完整的逆向工程:它是一个回显缓冲区。SNES DSP具有将先前样本的输出混合以产生回声效果的功能。这发生在声音生成过程的最后(最后一个声音阻止标志除外,该标志可用于关闭整个声音输出。)

通过以正确的测量时序编写代码并跟踪产生的回声,我们能够确定DSP生成的确切操作顺序每个样本,创造出完美的声音和节拍精度。

保存PPU


所有这些使我们进入了SNES体系结构方案的最后一部分:PPU-1和PPU-2芯片。感谢John McMaster,我们对S-PPU1(修订版1)和S-PPU2(修订版3)芯片的扫描增加了20倍。


第一个PPU SNES晶体的20倍扫描...


...和第二个PPU。

两次晶体扫描都让我们知道,这些芯片显然不是通用CPU,也不是专门的架构,它们从固件程序的内部ROM执行操作代码。这些是带有硬编码逻辑的独立逻辑电路,它们从不同的寄存器和存储器接收输入信号,并一次向监视器生成一条视频信号到一个光栅行。

PPU仍然是仿真SNES的最后障碍,因为与上述所有组件不同,PPU 实际上是一个黑匣子。我们可以将它们配置为任何状态,但是SNES CPU无法直接监视它们生成的内容。

如果我们使用前面的例子以乘法作为类比,请想象您请求的结果为3 * 7,但是在屏幕上得到的数字不是21,而是二进制的模糊模拟图像。任何运行您的软件的人都将看到 21,但您无法编写测试程序来自动检查他是否看到正确的答案。一个人对这种结果的人工验证不能扩展到超过数千次测试,并且将需要数百万次才能最大化PPU行为。

我知道您的想法:“但是,使用捕获卡,执行图像处理,将其与仿真器数字屏幕上的图像进行大致比较,并以此为基础进行测试会更容易吗?”

好吧,是的,有可能!特别是如果测试是要检查占据整个屏幕的两个巨大数字。

但是,如果测试有很多细微差别,并且我们试图识别一个像素的半色调的色差,该怎么办?如果我们想按顺序运行一百万个测试,而我们并不总是知道会生成什么,但仍想将结果与仿真输出进行比较,该怎么办?

数字数据无可比拟的便利性和准确性-只能匹配或不匹配的精确比特流。CRT信号的模拟性质无法为我们提供此功能。

它为什么如此重要?


除了一个游戏(Air Strike Patrol)以外,所有官方授权的SNES软件(应该是)都基于栅格字符串。这些游戏不会尝试在当前渲染的栅格线的中间更改PPU渲染的状态(程序员的这种技巧称为“光栅效果”)。这意味着绝大多数游戏的执行时间不必特别准确;如果您有时间下一个完整的栅格线,则一切正常。

但这对于单个游戏很重要。




这一系列图像显示了空袭巡逻队的“好运”消息中使用的复杂模拟效果

在上图中,您会看到Air Strike Patrol的逐帧文本“ Good Luck” 。游戏通过更改垂直滚动背景层3(BG3)的位置来实现它。但是,左侧的仪表板显示(您可以看到玩家拥有39枚导弹)也位于同一背景层上。

游戏通过在渲染左侧仪表板之后,但在文本“ Good Luck”开始渲染之前,通过更改每个光栅行中BG3滚动条的位置来实现这种分离。之所以可以这样做,是因为在仪表板和文本之外,BG3是透明的,并且无论垂直滚动寄存器的值如何,在这两个点之间都没有绘制内容。此行为向我们表明,滚动寄存器可以在渲染的任何阶段进行更改。


飞机下方的这个小阴影给那些痴迷于精密仿真器的开发人员带来了很多麻烦。

上图显示了飞机臭名昭著的阴影。通过在五条光栅线上的短纹波更改屏幕亮度寄存器,可以实现此效果。

在游戏过程中,您可以看到此阴影非常混乱。在上图中,它看起来有点像字母“ c”,但是它在每条光栅线中的形状随着每帧的长度和起点而变化。空袭巡逻队的开发人员只是大致勾勒出阴影应出现的位置,并直接解决了这个问题。在大多数情况下,这可行。

对这种行为的正确仿真需要完美的时机,这在仿真器中绝对是极其困难


在“ 空袭巡逻”暂停屏幕上,使用的光栅效果是其他任何SNES游戏中没有故意使用的。

现在让我们谈谈暂停屏幕。它打开BG3的同时在左侧绘制一个黄黑色边框,并在右侧的同一边框期间再次关闭它以在屏幕上绘制灰色线。他还通过帧切换交替显示其中显示这些灰线的光栅线,以产生叠加抖动的效果。

如果放大上面显示的仿真图像,您会注意到在这些灰线的左上角的一对光栅线中,有几个丢失的像素。发生这种情况是因为我的PPU仿真在时钟周期中不是100%完美。在这种情况下,它会导致启用BG3的时间晚于应有的时间。

我可以很容易地更改时间,以使图像正确呈现。但是这种更改可能会对其他游戏产生不利影响,这些游戏会更改光栅线中间的PPU显示寄存器。尽管空袭巡逻是唯一故意这样做的游戏,但至少有十几个游戏是偶然发生的(也许IRQ迟早会在其中射击)。

有时,这会对图片造成短暂的明显损坏,而在开发过程中则不会引起注意(例如,在Full Throttle Racing中)在商店和游戏之间的过渡期间)。有时在屏幕透明的情况下执行记录,因此不会引起视觉异常(例如,如在Dai Kaijuu Monogatari II中显示HP状态的情况。)但是即使这样的“不可见”边界情况也可能会导致光栅线渲染的准确性降低。在生产力最高的仿真器中使用。

即使您忽略Air Strike Patrol,SNES软件中的所有这些随机(但有效)栅格效果也不允许您在功能上设计能够以完美的时钟精度生成整个栅格线的PPU渲染器。

对于经过数年反复试验的bsnes,我们创建了具有“光栅效果”的此类游戏的列表。我们还创建了单独的渲染位置,这些渲染位置允许基于栅格线更快地渲染以正确显示所有这些游戏(当然,除了空袭巡逻队以外)。但从本质上讲,这是一堆针对特定游戏设计的令人讨厌的骇客。

我还有一个基于时钟的PPU渲染器,不需要所有这些技巧,但是有时会与真实设备的渲染产生微小的差异(一到四个像素),如上方的Air Strike Patrol的屏幕截图所示

内部锁存器


所有这些小失误的原因归结为紧迫的时机。

假设SNES渲染了其著名的模式7,这是一种仿射纹理转换,其中每条栅格线中的参数都发生了变化。要确定任何屏幕像素,您需要执行类似的计算:

px = a *片段(hoffset-hcenter)+ b *片段(voffset-vcenter)+
b * y +(h << << 8)

py = c *片段(hoffset-hcenter)+ d *片段(voffset-vcenter)+
d * y +(vcenter << 8)

对于帧中渲染的每个像素,Real SNES将无法足够快地完成所有这六个乘法。但是这些值对于每个像素都不会改变(或者至少不应改变),因此我们只需要在每条光栅线的开头计算一次px和py。也就是说,PPU将静态结果缓存在锁存器中,锁存器本质上是PPU寄存器的副本。将来,它们可以进行更改或保持不变。

然后,x,y坐标通过模式7进行如下转换:

牛=(px + a * x)>> 8

oy =(py + c * x)>> 8

尽管x随每个像素而变化,但我们知道每次都会执行一次递增。多亏了内部驱动器的存储,我们可以为每个像素简单地向ox和oy添加常数a和c,而不是为每个像素执行两次乘法。

那么问题就摆在我们面前:PPU在时钟周期的哪个特定位置从CPU可以访问的外部PPU寄存器中读取a和c的值?

如果我们太早采用它们,那么这可能会破坏一些游戏。如果我们为时已晚,它可能会破坏其他游戏。

最简单的方法是等待错误报告并调整这些位置,以解决每个特定游戏中的问题。但是在这种情况下,我们将永远找不到确切的位置,只能找到它们的近似值。

每次我们更改这些变量之一时,对我们重新测试SNES库中的所有三千半游戏来检测我们的更改可能导致的恶化是不现实的。

从煎锅里掉进火里



对模拟错误进行消除的艺术解释。

一种类似的测试方法风格,即“我们将不惜一切代价制作出我们感兴趣的游戏”导致了这种现象,我称这种现象为“从火到火”。

在SNES仿真开发的最初阶段,当游戏中出现问题时,该游戏中允许其工作的任何修正均被接受并添加到仿真器中。此修复程序肯定会破坏其他游戏。然后他们改正了这场比赛,之后第三场比赛失败了。修复第三局再次打破了第一局。这种情况持续了很多年。

这里的错误是开发人员试图一次只考虑一个变量。假设我们有一个游戏,并且要使它起作用,事件必须在20到120小节之间发生。我们不知道确切的小节,因此仅在中间选择70。

稍后,我们在另一个游戏中得到一个错误报告,并确定游戏能够正常工作,度量值应该在10到60之间。所以现在我们将其更改为40,这对两个游戏都适用。听起来合乎逻辑!

但是随后出现第三局,其中该事件应该在80到160之间进行!现在,我们无法使所有三款游戏具有相同的价值同时工作。

这迫使仿真器开发人员为特定游戏创建黑客。编码人员不希望发布无法运行MarioZeldaMetroid的仿真器因此,在一般情况下,使用时钟周期40,但是在加载“ 银河战士”时,我们将计时值强制为100。这

怎么可能,为什么两个游戏需要不同的值?发生这种情况是因为此处不仅涉及一个变量。您以前用来触发另一个事件的时间可能会影响下一个事件所需的时间值

想象一下简单的代数表达式:

2x + y = 120

您可以通过x = 10,y = 100来解决。或x = 20,y = 80。或x = 30,y = 60。如果我们只考虑x的值(它允许您同时运行一组游戏),那么我们会错过这样一个事实,即问题可能出在错误的y上!

增强兼容性的仿真器的第一个版本只是根据正在运行的游戏重新定义了x的值。即使后来发现了正确的x单个值,这种单独的游戏攻击仍然存在。因此,y问题将永远无法解决!

但是,对于SNES,不会同时涉及一个或两个变量。仅SNES控制台PPU就有52个外部寄存器,这大约是130个参数。在绘制单个栅格线的过程中,涉及所有这些参数的130个以及未知数量的内部寄存器和锁存器。对于外面的人来说,这是太多的信息,无法在特定的时间点实现PPU的完整状态。

对于初学者来说,仿真的这一方面并不明显,但是这很公平:准确性不等于兼容性。我们可以创建一个具有99%精度的模拟器,能够运行10%的游戏。而且您可以编写一个运行98%游戏的80%准确模拟器。有时,在短期内正确实施可能会破坏热门游戏。如果要同时实现100%的准确性 100%的兼容性,这是必要的牺牲

解决这个问题


由于演绎推理和实际结果,我们进入了PPU仿真的当前阶段。

我们知道两个PPU可以访问两个VRAM芯片。我们知道,他们可以从每个芯片读取每条光栅线已知数量的数据字节。我们知道每种SNES视频模式如何工作的大致细节。基于此,我们可以概述体系结构外观的通用模式。例如,这是前三个SNES视频模式如何工作的简短示例:

如果(io.bgMode == 0){

bg4.fetchNameTable();

bg3.fetchNameTable();

bg2.fetchNameTable();

bg1.fetchNameTable();

bg4.fetchCharacter(0);

bg3.fetchCharacter(0);

bg2.fetchCharacter(0);

bg1.fetchCharacter(0);

}

如果(io.bgMode == 1){

bg3.fetchNameTable();

bg2.fetchNameTable();

bg1.fetchNameTable();

bg3.fetchCharacter(0);

bg2.fetchCharacter(0);

bg2.fetchCharacter(1);

bg1.fetchCharacter(0);

bg1.fetchCharacter(1);

}

如果(io.bgMode == 2){

bg2.fetchNameTable();

bg1.fetchNameTable();

bg3.fetchOffset(0);

bg3.fetchOffset(8);

bg2.fetchCharacter(0);

bg2.fetchCharacter(1);

bg1.fetchCharacter(0);

bg1.fetchCharacter(1);

}


PPU仅向第三方观察者显示其状态的一小部分:水平和垂直向后(水平/垂直消隐)标志,水平和垂直像素数以及子画面间隔中的图块覆盖标志。这还不算什么,但是我重复一遍-观察者可以访问的状态的每个微小元素都可以帮助我们。

渲染期间,PPU芯片的VRAM(视频RAM,视频内存)对SNES CPU都是关闭的,即使读取也是如此。但事实证明,OAM(子画面存储器)和CGRAM(调色板存储器)是打开的。诀窍在于,此时,PPU控制地址总线。因此,在屏幕渲染期间读取OAM和CGRAM时,我可以观察到在如此关键的时刻PPU从这两个内存块中得到什么。

这些并不是难题的全部,但它们足以让我能够实现实际上正确的模式以获得子画面。

使用开放OAM和CGRAM的访问模式,PPU标志,来自不同游戏的错误报告的一般观察(即猜测)以及演绎推理,我们能够创建基于时钟的PPU渲染器,几乎可以完美启动所有已发布的游戏。

但是这种情况仍然pre可危:如果有人开始使用准确的刻度和光栅效果定时来创建自制游戏,那么我们所有的现代仿真器将无法处理这一问题。包括基于FPGA的软件和硬件实现。

我必须清楚地说:今天一切他们只知道SNES控制台的PPU芯片中的内部操作顺序和快照行为。没有人知道如何完美地模仿它们。至少现在(是。

可能的解决方案


我们该怎么办?从SNES CPU的角度来看,这是一个“黑匣子”,如何确定PPU中确切的操作顺序?

我看到四个可能的选择:逻辑分析仪,测试模式下的数字视频输出,提升板和从芯片上取下盖子。

逻辑分析仪


如果查看上面显示的PPU晶体的扫描,您会注意到芯片边缘的黑色区域。这些是连接到芯片触点的平台。

这些引脚在每个时钟周期内存储PPU芯片的状态。在这里,您可以找到芯片访问视频存储芯片的当前地址,从一个PPU传输到第二个PPU的数据的值等等。

该信息不适用于在SNES CPU上运行的代码,但是它提供了有关PPU操作内部顺序的有价值的观察。


将Super NES控制台PPU连接到类似的逻辑分析仪可能是黑匣子的关键。

逻辑分析仪的关键问题是,它们管理起来不太方便:如果您尝试从工作系统中采样实时数据,我们将得到一连串难以理解的结果。如果尝试分析系统的模拟RGB输出,将会遇到相同的问题:要捕获此数据,您将必须手动执行每个测试。这样的系统对于创建可再现的自动回归测试不是很好。

测试模式下的数字视频输出


最近,通过以20倍放大率扫描晶体切片,在SNES控制台PPU芯片中发现了秘密测试模式。如果您进行了小的硬件修改,则PPU将开始输出15位数字RGB信号

这几乎是我们所需要的!但是,该模式存在问题,因为著名的模式7不能在其中显示正确的图片。似乎此功能尚未完全完成。

另外,要实现此方法,仍需要手动修改SNES控制台以及在测试模式下捕获和分析输出的适当机制。但是,与捕获模拟RGB信号的解决方案不同,可以自动测试这样的数字信号,这可以使我们快速完成PPU逆向工程上的大量工作。

冒口


鉴于PPU是静态的,我们可以从工作的SNES控制台中取出PPU芯片,并将它们与两个VRAM芯片一起连接至原型板或定制电路板。之后,您可以在PPU和USB接口之间放置一个微控制器,并将该接口连接到PC,这将允许编码器对所有外部视频存储寄存器和PPU进行编程。此外,编码器将能够手动控制PPU时钟周期,并在每个时钟周期读取I / O连接器,寄存器以及PPU存储器中的结果信号。

通过修改软件仿真器,使其生成相同的I / O连接器内部值,我们甚至可以实时地将实际硬件与仿真进行比较。但是,这将是非常艰巨的工作,因为我们尚无法看到内部PPU操作。

移除封面


最后,最极端的解决方案是通过去除芯片盖来进一步研究晶体。我们已经有20倍放大的晶体扫描,但是它们的分辨率不足以像Visual 6502项目中那样分析和重新创建单个逻辑电路如果我们能够以100倍的放大率获得两个PPU的晶体扫描图,那么我们就可以开始艰苦的工作来编译PPU电路并将其转换为连接表或VHDL代码。然后,它们可以直接在FPGA中使用,也可以移植到C ++或另一种适用于创建软件仿真器的编程语言。

一位曾经做过此事的专家给了我一个粗略的估计:绘制两个PPU大约需要600个小时。该任务远远高于“让我们通过募捐和向某人付款来筹集资金”的水平,并且理想情况下属于“我们希望非常有才能的人会自愿帮助我们”。

当然,这并不意味着我不愿意在财务上奖励某人的帮助,我可以为必要的细节和工作付费。

寻求帮助


总结一下:我尽可能地参加了SNES模拟器项目,并且需要帮助来完成此最终任务。如果您已经读完了,那么您可能需要帮助!任何支持,包括参与GitHub上的bsnes项目或任何有关PPU芯片内部操作的研究文档,对我们来说都是无价之宝!

感谢您的阅读和支持!成为SNES仿真社区的成员,这对我来说是十五年来的荣幸。

All Articles