仿真器如何工作以及如何编写?[关闭]


968

模拟器如何工作?当我看到NES / SNES或C64模拟器时,我感到非常惊讶。

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

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

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


15
您需要找到的最重要的内容是该系统的“程序员手册”,因为它详细说明了硬件供应商与程序员之间的“合同”,并隐藏了不相关且可能更改的细节。您的机会取决于系统的普及程度。
Uri

155
好的游戏选择。
克里斯蒂安罗莫


16
对于任何想知道“ 仿真与仿真”的人
Lazer,2010年

8
自从我第一次玩游戏以来,我一直想知道为什么Hyrule到处都是“ 8-Ball”巨石:-)
Vivian River

Answers:


1124

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

基本思路:

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

处理器仿真:

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

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

使用所有这些路径,您的总体目标是相同的:执行一段代码来修改处理器状态并与“硬件”进行交互。处理器状态是给定处理器目标的处理器寄存器,中断处理程序等的集合。对于6502,你有一个数代表寄存器8位整数的:AXYP,和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也引起了我的注意,它包含一个很大的系统文档存储库,对于仿真目的非常有用。我没有花很多时间在这里,但是看起来他们有很多很棒的资源。

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


37
这已经准备好成为史诗般的答案。如果您能在最后指出我任何资源,也将不胜感激。我正在研究可能要模拟的SNES或NES系统,并将其作为我的学期项目。
mmcdole

8
当然。我将整理一份不错的资源清单。如果你们有任何特定要求,我会尽力满足他们的要求。
科迪·布罗西斯

3
@thenonhacker,我的资源部分中引用的IronBabel项目是我的。(标记了无耻的插头;))
Cody Brocious 09年

1
“已经证明在给定的二进制文件中找到所有代码等同于停止问题” –请参考?还是应该是“已经证明在任何给定的二进制文件中找到所有代码等同于停止问题”?也无法访问Steil的论文:-(
squelart

4
您提到您正在写书;您能给我们更新一下吗?我想对它感兴趣。
alex 2012年

126

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

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


43

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

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

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

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

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

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


1
仿真和仿真之间有什么区别?
胡虎2010年

2
@Wei:一般而言,仿真器应该像其仿真的系统一样在“外部”运行,但是没有什么可以说必须以类似的方式实现的。以模仿模拟系统的方式实现模拟器,因此其行为类似于模拟系统。
乌里(Uri)2010年

当您看到“仿真器”时,认为它与仿真器“仿真”时的相似之处
mP。


29

我知道这个问题有点老了,但是我想在讨论中添加一些内容。这里的大多数答案都围绕着仿真器,这些仿真器解释了它们所仿真系统的机器指令。

但是,有一个非常著名的例外,称为“ UltraHLE”(WIKIpedia文章)。UltraHLE是有史以来最著名的模拟器之一,在人们普遍认为Nintendo 64游戏无法做到的情况下,它可以模拟商业Nintendo 64游戏。实际上,在创建UltraHLE时,任天堂仍在为Nintendo 64制作新游戏!

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

UltraHLE的概念是通过模拟C库调用而不是机器级别的调用来使不可能成为可能。


22

值得一看的是Imran Nazar尝试用JavaScript 编写Gameboy模拟器。


1
我们如何获得Gameboy游戏的原始操作码指令?
Pacerier,2013年

在“灰色市场”上有许多设备可以出售。您不会在发达国家的任何大型商店中找到它们。这些设备能够将指令从游戏卡带复制到通常称为“ ROM”的文件中。Google“ Gameboy Roms”,但要提防工作不安全的链接和攻击站点!
维维安河

18

创建了我自己的80年代BBC微型计算机的仿真器(在Google中输入VBeeb),有很多事情要知道。

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

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


17

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

一种简单的方法是一对一解释指令。效果很好,但是很慢。更快的方法是重新编译-将源机器代码转换为目标机器代码。这更加复杂,因为大多数指令不会一对一映射。取而代之的是,您将不得不进行涉及其他代码的精心设计的变通办法。但是最终它要快得多。大多数现代仿真器都这样做。


1
最糟糕的是,到目前为止缺少文档。当您发现GameBoy Color中经过修改的Z80核心具有未记录的标志操作,即您正在测试的游戏使用该标志操作时,您真的开始失去信心了。
卡勒姆·罗杰斯

1
Pet peeve:是机器代码(单数),而不是机器代码(复数);就像是摩尔斯电码而不是摩尔斯电码一样
劳伦斯·多尔

1
@Vilx:实际上不是-自从软件问世以来,一直在使用术语“机器代码”,指的是为CPU设置的指令,并且不是复数形式。它指的是“指令 ”,单数形式,而不是复数形式的“指令”。与程序代码,莫尔斯电码等相同。复数形式的使用已避免了滥用,通常是那些以英语为第二语言的人滥用。
劳伦斯·多尔

1
@Software Monkey-但是我不能使用单词“代码”来指代集合中的单个项目吗?例如:“ ... --- ...-这三个摩尔斯电码代表三个字母S,O,S。” 因为...是代表字母“ S” 的代码。没有?
Vilx- 2011年

1
不,代码是不可数名词,它没有复数形式像水或沙子..
伊万

15

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

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

您应该开始为simpe系统编写模拟器,例如老式的Game Boy(使用Z80处理器,我不会误会)或C64。


9
C64一个“简单”的系统?虽然6510是比较简单(一旦你已经涵盖了未上市的操作码),声音(SID)和视频(VIC)芯片是什么,简单。为了达到任何不错的兼容性,您需要模拟它们-硬件错误以及所有错误。
moobaa



7

我从来没有做过像模拟游戏机那样花哨的事情,但是我确实参加了一次课程,其中的任务是为Andrew Tanenbaums 结构化计算机组织中描述的机器编写一个模拟器 。这很有趣,给了我很多哈哈的时间。您可能需要先整理本书,然后再着手编写真正的模拟器。


4

关于模拟真实系统或您自己的东西的建议?我可以说仿真器通过仿真整个硬件来工作。也许不是下降到电路上(就像在硬件上移动位一样。移动字节是最终结果,因此复制字节就可以了)。模拟器很难创建,因为您需要模拟许多hack(如异常效果),时序问题等。如果一个(输入)错误,则整个系统可能崩溃,或者充其量只有一个错误/故障。


4

所述共享源设备仿真器包含可编译源代码以一个PocketPC / Smartphone模拟器(需要Visual Studio中,运行在Windows上)。我研究了二进制发行版的V1和V2。

它解决了许多仿真问题:-从来宾虚拟到来宾物理地址到主机虚拟的有效地址转换-来宾代码的JIT编译-仿真外围设备(例如网络适配器,触摸屏和音频)-UI集成,用于主机键盘和鼠标-保存/恢复状态,用于模拟从低功耗模式恢复


1

添加@Cody Brocious提供的答案
在虚拟化环境中,您要将新系统(CPU,I / O等)仿真到虚拟机,我们可以看到以下几种仿真器。

解释:bochs是解释器的一个示例,它是x86 PC仿真器,它将来自来宾系统的每条指令转换为另一组指令(主机ISA)以产生预期的效果。是的,它很慢,但没有不会缓存任何内容,因此每条指令都经过相同的周期。

动态仿真器:Qemu是一个动态仿真器。它确实进行了来宾指令的即时翻译,也缓存了结果。最好的部分是直接在主机系统上执行尽可能多的指令,从而加快了仿真速度。就像Cody提到的那样,它将代码分成多个块(1个单执行流)。

静态仿真器:据我所知,没有静态仿真器可以对虚拟化有所帮助。


1

我将如何开始仿真。

1.获取有关低级编程的书籍,您将需要Nintendo ...游戏男孩的“假装”操作系统使用的书籍。

2.专门获取有关仿真的书籍,以及有关OS开发的书籍。(您不会制作一个os,而是最接近它的。

3.查看一些开源模拟器,尤其是要为其创建模拟器的系统。

4.将更复杂的代码片段复制到您的IDE /编译器中。这将节省您编写长代码的时间。这是我为os开发所做的,使用linux分区


1

我写了一篇关于用JavaScript模拟Chip-8系统的文章。

因为系统不是很复杂,所以这是一个很好的起点,但是您仍然可以了解操作码,堆栈,寄存器等的工作方式。

我将很快为NES撰写更长的指南。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.