小编典典

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

all

模拟器是如何工作的?当我看到 NES/SNES 或 C64 仿真器时,我感到很震惊。

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

您是否必须通过解释其特定的汇编指令来模拟这些机器的处理器?还有什么内容?它们通常是如何设计的?

您能给有兴趣编写模拟器(尤其是游戏系统)的人提供任何建议吗?


阅读 136

收藏
2022-02-28

共1个答案

小编典典

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

基本思路:

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

处理器仿真:

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

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

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

处理器重新编译参考:

附录:

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

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

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

我很高兴这篇文章对我有所帮助,我希望我能在今年年底/明年年初之前完成我关于这个主题的书。

2022-02-28