协慌网

登录 贡献 社区

模拟器如何工作以及它们是如何编写的?

模拟器如何工作?当我看到 NES / SNES 或 C64 模拟器时,它让我震惊。

http://www.tommowalker.co.uk/snemzelda.png

您是否必须通过解释其特定的装配说明来模拟这些机器的处理器?还有什么进入它?它们通常是如何设计的?

你能为有兴趣编写模拟器(特别是游戏系统)的人提供建议吗?

答案

仿真是一个多方面的领域。以下是基本思想和功能组件。我要把它分成几块,然后通过编辑填写细节。我要描述的许多内容都需要了解处理器的内部工作原理 - 装配知识是必要的。如果我对某些事情有点过于模糊,请提出问题,以便我可以继续改进这个答案。

基本理念:

仿真通过处理处理器和各个组件的行为来工作。您构建系统的每个单独部分,然后像硬件中的电线一样连接这些部分。

处理器仿真:

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

  • 解释
  • 动态重新编译
  • 静态重新编译

通过所有这些路径,您可以拥有相同的总体目标:执行一段代码来修改处理器状态并与 “硬件” 进行交互。处理器状态是给定处理器目标的处理器寄存器,中断处理程序等的集合体。对于 6502,你有许多代表寄存器的 8 位整数: AXYPS ; 你还有一个 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 以大幅提高速度。

处理器重新编译参考:

  • 由 Michael Steil(上面引用)完成的静态重新编译研究在本文中达到高潮,你可以在这里找到源代码等。

附录:

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

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

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

我很高兴这篇文章很有帮助,我希望我可以在今年年底 / 明年年初完成这个问题。

一位名叫 Victor Moya del Barrio 的人写了关于这个话题的论文。 152 页的很多好消息。您可以在此处下载 PDF。

如果您不想在scribd注册,可以谷歌搜索 PDF 标题, “仿真编程技术研究” 。 PDF 有几个不同的来源。

仿真可能看起来令人生畏,但实际上比模拟更容易。

任何处理器通常都有一个编写良好的规范,用于描述状态,交互等。

如果您根本不关心性能,那么您可以使用非常优雅的面向对象程序轻松模拟大多数旧处理器。例如,X86 处理器需要某些东西来维护寄存器的状态(简单),维护内存状态(简单),以及将每个传入命令应用到机器当前状态的东西。如果你真的想要准确性,你也会模仿内存翻译,缓存等,但这是可行的。

实际上,许多微芯片和 CPU 制造商针对芯片的仿真器测试程序,然后针对芯片本身进行测试,这有助于他们发现芯片的规格是否存在问题,或者是硬件中芯片的实际实现。例如,可以编写会导致死锁的芯片规范,并且当硬件中出现截止日期时,重要的是看它是否可以在规范中再现,因为这表明比芯片实现中的问题更大的问题。

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

考虑到旧视频游戏(NES / SNES 等)的性能非常低,在现代系统上仿真非常容易。事实上,你可以下载一套有史以来的每一款 SNES 游戏或任何 Atari 2600 游戏更令人惊讶,因为当这些系统受欢迎时,可以自由访问每个墨盒,这将是梦想成真。