关闭。这个问题是off-topic .它目前不接受答案。
8年前关闭。
锁定。这个问题及其答案是locked因为这个问题是题外话,但具有历史意义。它目前不接受新的答案或互动。
模拟器如何工作?当我看到 NES/SNES 或 C64 模拟器时,我感到震惊。
您是否必须通过解释特定的汇编指令来模拟这些机器的处理器?里面还有什么?它们通常是如何设计的?
对于有兴趣编写模拟器(尤其是游戏系统)的人,您能提供什么建议吗?
最佳答案
仿真是一个多方面的领域。以下是基本思想和功能组件。我将把它分成几部分,然后通过编辑填写细节。我将要描述的许多事情都需要了解处理器的内部工作原理——汇编知识是必要的。如果我对某些事情有点过于含糊,请提出问题,以便我继续改进此答案。
基本思路:
仿真通过处理处理器和各个组件的行为来工作。您构建系统的每个单独部分,然后像硬件中的电线一样连接这些部分。
处理器仿真:
处理处理器仿真的方式有以下三种:
对于所有这些路径,您具有相同的总体目标:执行一段代码来修改处理器状态并与“硬件”交互。处理器状态是给定处理器目标的处理器寄存器、中断处理程序等的集合。对于 6502,您将有许多表示寄存器的 8 位整数:
A
, X
, Y
, P
, 和 S
;你还有一个 16 位 PC
登记。有了口译,您从
IP
开始(指令指针 - 也称为 PC
,程序计数器)并从内存中读取指令。您的代码解析此指令并使用此信息来更改处理器指定的处理器状态。解释的核心问题是它非常慢。每次处理给定指令时,都必须对其进行解码并执行必要的操作。使用动态重新编译,您可以像解释一样迭代代码,但不仅仅是执行操作码,您还可以构建一个操作列表。一旦到达分支指令,就将这个操作列表编译为主机平台的机器代码,然后缓存这个编译后的代码并执行它。然后当你再次命中给定的指令组时,你只需要从缓存中执行代码。 (顺便说一句,大多数人实际上并没有列出指令列表,而是将它们即时编译为机器代码——这使得优化变得更加困难,但这超出了本答案的范围,除非有足够多的人感兴趣)
使用静态重新编译,您可以执行与动态重新编译相同的操作,但您遵循分支。您最终构建了一个代表程序中所有代码的代码块,然后可以在没有进一步干扰的情况下执行这些代码。如果不是因为以下问题,这将是一个很好的机制:
这些结合起来使静态重新编译在 99% 的情况下完全不可行。有关更多信息,Michael Steil 对静态重新编译做了一些很棒的研究——这是我见过的最好的。
处理器仿真的另一面是您与硬件交互的方式。这真的有两个方面:
处理器时序:
某些平台——尤其是像 NES、SNES 等较旧的控制台——需要你的模拟器有严格的时间来完全兼容。使用 NES,您拥有 PPU(像素处理单元),它要求 CPU 在精确的时刻将像素放入其内存中。如果您使用解释,您可以轻松计算周期并模拟正确的计时;使用动态/静态重新编译,事情变得/很多/更复杂。
中断处理:
中断是 CPU 与硬件通信的主要机制。通常,您的硬件组件会告诉 CPU 它关心什么中断。这非常简单——当你的代码抛出一个给定的中断时,你查看中断处理程序表并调用正确的回调。
硬件仿真:
模拟给定的硬件设备有两个方面:
以硬盘驱动器为例。通过创建后备存储、读/写/格式化例程等来模拟该功能。这部分通常非常简单。
设备的实际界面要复杂一些。这通常是内存映射寄存器(例如,设备监视更改以执行信令的部分内存)和中断的某种组合。对于硬盘驱动器,您可能有一个内存映射区域,您可以在其中放置读取命令、写入等,然后读回这些数据。
我会更详细地介绍,但是您可以使用一百万种方法。如果您在此处有任何具体问题,请随时提问,我会添加信息。
资源:
我想我在这里给出了一个很好的介绍,但还有一个 吨 的额外区域。我很乐意帮助解决任何问题;由于极其复杂,我对其中的大部分内容都非常含糊。
强制性维基百科链接:
一般仿真资源:
模拟器项目引用:
处理器重编译引用:
附录:
自从提交这个答案已经一年多了,随着它受到的关注,我想是时候更新一些东西了。
也许现在仿真中最令人兴奋的事情是 libcpu ,由前面提到的 Michael Steil 开始。它是一个旨在支持大量 CPU 内核的库,这些内核使用 LLVM 进行重新编译(静态和动态!)。它具有巨大的潜力,我认为它会为仿真做出巨大贡献。
emu-docs也引起了我的注意,它包含一个很好的系统文档存储库,对于仿真目的非常有用。我在那里待的时间不多,但看起来他们有很多很棒的资源。
我很高兴这篇文章很有帮助,我希望我能在今年年底/明年年初之前完成我关于这个主题的书。
关于emulation - 模拟器如何工作以及它们是如何编写的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/448673/