为了账号安全,请及时绑定邮箱和手机立即绑定

仿真器如何工作以及如何编写?

仿真器如何工作以及如何编写?

守着星空守着你 2019-11-21 10:14:48
模拟器如何工作?当我看到NES / SNES或C64仿真器时,我感到非常惊讶。http://www.tommowalker.co.uk/snemzelda.png您是否需要通过解释特定的组装说明来模拟这些机器的处理器?还有什么呢?它们通常是如何设计的?您可以为有兴趣编写模拟器(尤其是游戏系统)的人提供任何建议吗?
查看完整描述

3 回答

?
森林海

TA贡献2011条经验 获得超2个赞

仿真是一个多方面的领域。这里是基本思想和功能组件。我将把它分成几部分,然后通过编辑填写细节。我将要描述的许多内容都需要了解处理器的内部工作原理-组装知识是必需的。如果我在某些方面不太清楚,请提出问题,以便我继续改进此答案。


基本思路:

通过处理处理器和各个组件的行为来进行仿真。您构建系统的各个部分,然后像连接硬件中的电线那样连接各个部分。


处理器仿真:

有三种处理处理器仿真的方法:


解释

动态重新编译

静态重新编译

使用所有这些路径,您的总体目标是相同的:执行一段代码来修改处理器状态并与“硬件”进行交互。处理器状态是给定处理器目标的处理器寄存器,中断处理程序等的集合。对于6502,你有一个数代表寄存器8位整数的:A,X,Y,P,和S; 您还将有一个16位PC寄存器。


通过解释,您可以从IP(指令指针-也称为PC程序计数器)开始,然后从内存中读取指令。您的代码将解析此指令,并使用此信息来更改处理器指定的处理器状态。解释的核心问题是它的速度很慢。每次处理给定指令时,都必须对其进行解码并执行必要的操作。


通过动态重新编译,您可以像解释一样迭代代码,但是不仅可以执行操作码,还可以构建操作列表。到达分支指令后,您可以将此操作列表编译为适用于您主机平台的机器代码,然后缓存此编译后的代码并执行它。然后,当您再次命中给定的指令组时,只需执行高速缓存中的代码即可。(顺便说一句,大多数人实际上并没有列出指令,而是将它们即时编译为机器代码,这使优化变得更加困难,但这超出了此答案的范围,除非足够的人对此感兴趣)


使用静态重新编译,您可以执行与动态重新编译相同的操作,但是要遵循分支。您最终构建了代表程序中所有代码的代码块,然后可以在没有更多干扰的情况下执行代码。如果不是以下问题,这将是一个很好的机制:


程序中不存在的代码(例如,压缩,加密,运行时生成/修改等)不会重新编译,因此不会运行

已经证明在给定的二进制文件中查找所有代码等同于停止问题

这些结合在一起使静态重新编译在99%的情况下完全不可行。有关更多信息,Michael Steil对静态重新编译进行了一些出色的研究,这是我见过的最好的。


处理器仿真的另一面是您与硬件交互的方式。这实际上有两个方面:


处理器计时

中断处理

处理器计时:

某些平台-尤其是NES,SNES等较旧的控制台-要求您的模拟器具有严格的时间安排以完全兼容。使用NES,您将拥有PPU(像素处理单元),它要求CPU在精确的时刻将像素放入其内存中。如果使用解释,则可以轻松地计算周期并模仿正确的时间;通过动态/静态重新编译,事情变得很复杂。


中断处理:

中断是CPU与硬件通信的主要机制。通常,您的硬件组件会告诉CPU它关心的是什么中断。这非常简单-当您的代码引发给定的中断时,您查看中断处理程序表并调用适当的回调。


硬件仿真:

模拟给定的硬件设备有两个方面:


模拟设备功能

模拟实际的设备接口

以硬盘驱动器为例。通过创建后备存储,读取/写入/格式化例程等来模拟该功能。这部分通常非常简单。


设备的实际接口要复杂一些。这通常是内存映射寄存器(例如,设备监视用于执行信令更改的内存部分)和中断的某种组合。对于硬盘驱动器,您可能有一个内存映射区域,您可以在其中放置读取命令,写入等,然后将这些数据读回。


我会更详细地介绍,但是您可以使用一百万种方法。如果您在此处有任何具体问题,请随时提出,我将添加信息。


资源:

我想,我已经给了一个很好的介绍在这里,但也有一吨的其他领域。我很乐意为您解答任何问题;由于巨大的复杂性,我在大多数情况下都非常模糊。


强制性维基百科链接:

仿真器

动态重新编译

通用仿真资源:

Zophar- 这是我从仿真开始的地方,首先下载仿真器,然后最终掠夺其庞大的文档档案。这是您可能拥有的绝对最佳资源。

NGEmu-没有很多直接资源,但是他们的论坛无与伦比。

RomHacking.net-文档部分包含有关流行控制台的机器体系结构的资源

仿真器项目参考:

IronBabel-这是.NET的仿真平台,使用Nemerle编写,可将代码即时编译为C#。免责声明:这是我的项目,请原谅。

BSnes-一款出色的SNES模拟器,旨在达到周期完美的准确性。

MAME - 的街机模拟器。很好的参考。

6502asm.com- 这是一个带有酷炫小论坛的JavaScript 6502模拟器。

dynarec'd 6502asm-这是我在一两天内做了的一个小技巧。我从6502asm.com取得了现有的模拟器,并对其进行了更改,以将代码动态地重新编译为JavaScript,从而大大提高了速度。

处理器重新编译参考:

该研究分为静态重新编译由迈克尔·斯泰尔(上面提到的)做过高潮本文,你可以找到源和这样的在这里。

附录:

自提交此答案以来已经过去了一年多的时间,并且得到了所有人的关注,我认为现在是时候更新一些东西了。


也许目前最令人兴奋的事情是libcpu,它是由前面提到的Michael Steil启动的。这是一个旨在支持大量CPU内核的库,这些内核使用LLVM进行重新编译(静态和动态!)。它具有巨大的潜力,我认为它将为仿真做出巨大贡献。


emu-docs也引起了我的注意,它包含一个很大的系统文档库,这对于仿真非常有用。我没有花很多时间在这里,但是看起来他们有很多很棒的资源。


我很高兴这篇文章对您有所帮助,并且希望我能在年底或明年年初完成有关该主题的书。


查看完整回答
反对 回复 2019-11-21
?
慕码人2483693

TA贡献1860条经验 获得超9个赞

仿真看似令人生畏,但实际上比仿真要容易得多。


任何处理器通常都具有写得很好的规范,用于描述状态,交互等。


如果您根本不关心性能,则可以使用非常优雅的面向对象程序轻松地模拟大多数较旧的处理器。例如,一个X86处理器需要某种东西来维护寄存器的状态(简单),某种东西来维护内存的状态(简单),并且需要接受每个传入命令并将其应用于计算机的当前状态。如果您确实想要准确性,那么您也可以模拟内存转换,缓存等,但这是可行的。


实际上,许多微芯片和CPU制造商会先对芯片的仿真器然后对芯片本身进行测试,这有助于他们找出芯片规格或芯片在硬件中的实际实现方面是否存在问题。例如,可以编写可能导致死锁的芯片规格,并且当硬件中出现最后期限时,务必查看是否可以在规格中复制该规格,因为这表明比芯片实现中的问题更大。


当然,用于视频游戏的模拟器通常关心性能,因此它们不使用幼稚的实现,并且还包括与主机系统的OS接口的代码,例如使用绘图和声音。


考虑到旧视频游戏(NES / SNES等)的性能非常慢,在现代系统上进行仿真非常容易。实际上,考虑到当这些系统流行时,免费访问每个卡带将是梦想成真的,您可以下载一套既有的SNES游戏,也可以下载任何Atari 2600游戏,这真是令人惊讶。


查看完整回答
反对 回复 2019-11-21
  • 3 回答
  • 0 关注
  • 456 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信