题 模拟器如何工作以及它们是如何编写的? [关闭]


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

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

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

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


969
2018-01-15 22:10


起源


您需要找到的最重要的事情是该系统的“程序员手册”,因为它详细说明了硬件供应商和程序员之间的“合同”,并隐藏了不相关且可能发生变化的细节。您的机会取决于系统受欢迎程度。 - Uri
好的比赛选择。 - Cristián Romo
是的,我相信如此: en.wikipedia.org/wiki/The_Legend_of_Zelda:_A_Link_to_the_Past - mmcdole
对于任何想知道的人 仿真与仿真 - Lazer
自从我第一次玩这个游戏以来,我一直想知道为什么Hyrule充斥着“8球”巨石:-) - Daniel Allen Langdon


答案:


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

基本理念:

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

处理器仿真:

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

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

通过所有这些路径,您可以拥有相同的总体目标:执行一段代码来修改处理器状态并与“硬件”进行交互。处理器状态是给定处理器目标的处理器寄存器,中断处理程序等的集合。对于6502,你有许多代表寄存器的8位整数: AXYP,和 S;你也有一个16位 PC 寄存器。

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

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

使用静态重新编译时,您可以执行与动态重新编译相同的操作,但是您可以使用分支。您最终构建了一段代表程序中所有代码的代码,然后可以执行这些代码而不会产生进一步的干扰。如果不是出于以下问题,这将是一个很好的机制:

  • 不在程序中开始的代码(例如,在运行时压缩,加密,生成/修改等)将不会被重新编译,因此它不会运行
  • 事实证明,找到给定二进制文件中的所有代码都等同于 停止问题

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

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

  • 处理器时序
  • 中断处理

处理器时间:

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

中断处理:

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

硬件仿真:

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

  • 模拟设备的功能
  • 模拟实际的设备接口

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

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

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

资源:

我想我在这里给了一个很好的介绍,但有一个  其他领域。我很乐意帮助解决任何问题;由于其极大的复杂性,我在大多数情况下都非常模糊。

必需的维基百科链接:

一般仿真资源:

  •    - 这是我开始模仿,首先下载模拟器并最终掠夺他们庞大的文档档案的地方。这是您可能拥有的绝对最佳资源。
  • 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-文档 我也注意到了一个很好的系统文档库,这对于仿真非常有用。我没有花太多时间在那里,但看起来他们有很多很棒的资源。

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


1127
2018-01-15 22:13



这已经准备好成为一个史诗般的答案。如果你可以指出我的任何资源,最后它将不胜感激。我正在寻找SNES或NES系统来模拟和制作我的学期项目。 - mmcdole
当然。我打算组建一个很好的资源列表。如果你们有任何具体要求,我会尽力填写。 - Cody Brocious
@thenonhacker,我的资源部分引用的IronBabel项目是我的。 (标记无耻的插头;)) - Cody Brocious
“已经证明找到给定二进制文件中的所有代码都等同于停止问题” - 请参考?或者它应该是“已经证明找到所有代码 任何 给定二进制相当于停止问题“?也无法访问Steil的论文:-( - squelart
你提到你正在写一本书;你能告诉我们一个更新吗?我是一个人,有兴趣阅读它。 - alex


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

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


126
2018-02-02 17:27





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

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

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

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

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

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


43
2018-01-15 22:43



仿真和模拟有什么区别? - Wei Hu
@Wei:一般来说,仿真器应该像它模拟的系统一样“外部”行事,但没有什么可说的,它必须以类似的方式实现。模拟器以模仿模拟系统的方式实现,因此表现得像它一样。 - Uri
当你看到“模拟器”认为它类似而模拟器“模拟” - mP.
@WeiHu,看 stackoverflow.com/questions/1584617/... - Pacerier


我知道这个问题有点陈旧,但我想在讨论中加入一些内容。这里的大部分答案都围绕着模拟器解释他们模拟的系统的机器指令。

然而,有一个非常着名的例外,称为“UltraHLE”(WIKIpedia文章)。 UltraHLE是有史以来最着名的模拟器之一,它被广泛认为是不可能的,仿效商用Nintendo 64游戏(在家用电脑上具有不错的性能)。事实上,当UltraHLE诞生时,任天堂仍在为Nintendo 64制作新游戏!

我第一次看到有关印刷杂志中模拟器的文章,之前我曾在网上讨论过它们。

UltraHLE的概念是通过模拟C库调用而不是机器级调用来实现不可能的。


29
2017-07-07 18:17





值得一看的是伊姆兰纳扎尔试图写一篇文章 掌上游戏机 JavaScript中的模拟器。


22
2017-11-11 10:19



我们如何获得Gameboy游戏的原始操作码指令? - Pacerier
“灰色市场”上有许多可供销售的设备。你不会在发达国家的任何主要商店找到它们。这些设备能够将游戏盒中的指令复制到通常称为“ROM”的文件中。谷歌“Gameboy Roms”,但要注意工作不安全的链接和攻击网站! - Daniel Allen Langdon


创建了我自己的80年代BBC微型计算机模拟器(将VBeeb输入Google),有很多事情需要了解。

  • 你不是仿效真实的东西,那将是一个复制品。相反,你在模仿 。一个很好的例子是计算器,真实的东西有按钮,屏幕,外壳等。但是要模拟计算器,你只需要模拟按钮是向上还是向下,LCD的哪些部分打开等等。基本上,一组数字表示可以在计算器中更改的所有可能的组合。
  • 您只需要模拟器的界面出现并表现得像真实的东西。这更令人信服的是仿真越接近。幕后发生的事情可以是你喜欢的任何事情。但是,为了便于编写仿真器,在真实系统(即芯片,显示器,键盘,电路板和抽象计算机代码)之间存在心理映射。
  • 要模拟计算机系统,最简单的方法是将其分解为更小的块并单独模拟这些块。然后将整批产品串在一起作为成品。就像一组带输入和输出的黑盒子一样,它非常适合面向对象的编程。您可以进一步细分这些块以使生活更轻松。

实际上,您通常希望编写速度和仿真保真度。这是因为目标系统上的软件(可能)运行速度比源系统上的原始硬件慢。这可能会限制编程语言,编译器,目标系统等的选择。
此外,您必须限制您准备模拟的内容,例如,不必模拟微处理器中晶体管的电压状态,但可能需要模拟微处理器寄存器组的状态。
一般来说,仿真的细节程度越小,您对原始系统的保真度就越高。
最后,旧系统的信息可能不完整或不存在。所以掌握原始设备是至关重要的,或者至少要珍惜别人写的另一个好模拟器!


18
2017-08-05 15:36





是的,你必须“手动”解释整个二进制机器代码混乱。不仅如此,大多数时候您还必须模拟一些在目标机器上没有等效物的奇特硬件。

简单的方法是逐个解释说明。这很好用,但速度很慢。更快的方法是重新编译 - 将源机器代码转换为目标机器代码。这更复杂,因为大多数指令不会一对一映射。相反,您将不得不进行涉及其他代码的复杂解决方案。但最终它要快得多。大多数现代模拟器都这样做。


17
2018-01-15 22:17



最糟糕的是到目前为止缺少文档。当您发现GameBoy Color中经过修改的Z80核心具有未记录的标记操作时,您正在测试的游戏使用了您真正开始失去信心。 - Callum Rogers
宠儿:这是机器 码 (单数),不是机器 代码 (复数);就像它一样 摩尔斯电码 不 莫尔斯电码。 - Lawrence Dol
@Vilx:实际上没有 - 术语“机器代码”,指的是CPU的指令集,自软件开始以来就一直在使用,而不是复数。它指的是“指令 组“,单数形式,而不是复数形式的”指令“。与程序代码,摩尔斯电码等相同。复数形式的使用已经从滥用中悄悄进入,通常是那些将英语作为第二语言的人。 - Lawrence Dol
@Software Monkey - 但是我不能用“代码”这个词来引用集合中的单个项目吗?例如: ”... --- ...  - 这三个摩尔斯电码代表三个字母S,O,S“因为 ... 是 一个代码 代表字母“S”。没有? - Vilx-
不,代码是不可数名词,它没有像水或沙子这样的复数形式。 - Ivan


在开发仿真器时,您将解释系统正在处理的处理器组件(Z80,8080,PS CPU等)。

您还需要模拟系统具有的所有外围设备(视频输出,控制器)。

您应该开始为simpe系统编写模拟器,就像旧的一样 掌上游戏机 (使用Z80处理器,我不是不误)或者是C64。


15
2018-01-15 22:21



C64是一个“简单”的系统?虽然6510相对简单(一旦你覆盖了未列出的操作码),声音(SID)和视频(VIC)芯片都是什么 但 简单。要实现任何相当级别的兼容性,您需要模拟它们 - 硬件错误等等。 - moobaa