模拟器是如何工作的?当我看到NES/SNES或C64模拟器时,我很震惊。
您是否必须通过解释特定的组装指令来模拟这些机器的处理器?还有什么?它们通常是如何设计的?
你能给那些对编写模拟器(特别是游戏系统)感兴趣的人一些建议吗?
模拟器是如何工作的?当我看到NES/SNES或C64模拟器时,我很震惊。
您是否必须通过解释特定的组装指令来模拟这些机器的处理器?还有什么?它们通常是如何设计的?
你能给那些对编写模拟器(特别是游戏系统)感兴趣的人一些建议吗?
当前回答
是的,你必须“手工”解释整个二进制机器码的混乱。不仅如此,大多数情况下,您还必须模拟一些在目标机器上没有等效硬件的外来硬件。
简单的方法是一个接一个地解释指令。这工作得很好,但是很慢。一种更快的方法是重新编译——将源机器码转换为目标机器码。这比较复杂,因为大多数指令都不是一对一映射的。相反,您将不得不制定涉及额外代码的详细变通方案。但最终还是要快得多。大多数现代模拟器都是这样做的。
其他回答
仿真是一个多方面的领域。下面是基本思想和功能组件。我将把它分成几部分,然后通过编辑填充细节。我将要描述的许多东西都需要了解处理器的内部工作原理——组装知识是必要的。如果我在某些事情上有点太模糊,请提问,这样我可以继续改进这个答案。
基本思想:
仿真通过处理处理器和各个组件的行为来工作。您构建系统的每个单独部分,然后将这些部分连接起来,就像硬件中的电线一样。
处理器仿真:
有三种处理处理器仿真的方法:
解释 动态重新编译 静态重新编译
使用所有这些路径,您有相同的总体目标:执行一段代码来修改处理器状态并与“硬件”交互。处理器状态是给定处理器目标的处理器寄存器、中断处理程序等的集合体。对于6502,你有一些8位整数表示寄存器:a, X, Y, P和S;你还会有一个16位的PC寄存器。
通过解释,从IP(指令指针——也称为PC,程序计数器)开始,从内存中读取指令。您的代码解析此指令并使用此信息更改处理器指定的处理器状态。解释的核心问题是它非常慢;每次处理给定指令时,都必须对其进行解码并执行必要的操作。
With dynamic recompilation, you iterate over the code much like interpretation, but instead of just executing opcodes, you build up a list of operations. Once you reach a branch instruction, you compile this list of operations to machine code for your host platform, then you cache this compiled code and execute it. Then when you hit a given instruction group again, you only have to execute the code from the cache. (BTW, most people don't actually make a list of instructions but compile them to machine code on the fly -- this makes it more difficult to optimize, but that's out of the scope of this answer, unless enough people are interested)
对于静态重新编译,您所做的与动态重新编译相同,但是您遵循分支。最终构建的代码块代表程序中的所有代码,然后可以在没有进一步干扰的情况下执行。如果没有以下问题,这将是一个很棒的机制:
不在程序中开始的代码(例如压缩、加密、运行时生成/修改等)不会被重新编译,因此不会运行 已经证明,在给定的二进制文件中找到所有的代码等同于停止问题
这些结合在一起使得静态重新编译在99%的情况下完全不可行的。要了解更多信息,Michael Steil对静态重新编译做了一些很好的研究——这是我所见过的最好的研究。
处理器模拟的另一方面是与硬件交互的方式。这确实有两面性:
处理器时间 中断处理
处理器时间:
某些平台——尤其是NES、SNES等较老的主机——要求你的模拟器具有严格的兼容时间。对于NES,您有PPU(像素处理单元),它要求CPU在精确的时刻将像素放入内存。如果你使用解释,你可以很容易地计算周期并模拟适当的计时;对于动态/静态重新编译,事情要复杂得多。
中断处理:
中断是CPU与硬件通信的主要机制。通常,硬件组件会告诉CPU它所关心的中断。这非常简单——当您的代码抛出一个给定的中断时,您查看中断处理程序表并调用适当的回调。
硬件仿真:
模拟一个给定的硬件设备有两个方面:
模拟设备的功能 模拟实际的设备接口
以硬盘为例。通过创建备份存储、读/写/格式化例程等来模拟该功能。这部分通常非常简单。
设备的实际界面要复杂一些。这通常是内存映射寄存器(例如,设备观察变化以发出信号的内存部分)和中断的某种组合。对于硬盘驱动器,您可能有一个内存映射区域,您可以在其中放置读取命令,写入等,然后读取此数据。
我想说得更详细一些,但你有很多方法可以用它。如果你有任何具体的问题,请提出来,我会补充信息。
资源:
我认为我在这里做了一个很好的介绍,但是还有很多其他的领域。我非常乐意帮助解答任何问题;我在这方面的大部分内容都很模糊,因为它非常复杂。
强制性的维基百科链接:
模拟器 动态重新编译
一般仿真资源:
Zophar—这是我开始模拟的地方,首先下载模拟器,最终掠夺它们巨大的文档档案。这绝对是你能拥有的最好的资源。 NGEmu——没有太多直接资源,但他们的论坛是无敌的。 RomHacking.net—文档部分包含有关流行控制台的机器架构的资源
模拟器项目参考:
IronBabel -- This is an emulation platform for .NET, written in Nemerle and recompiles code to C# on the fly. Disclaimer: This is my project, so pardon the shameless plug. BSnes -- An awesome SNES emulator with the goal of cycle-perfect accuracy. MAME -- The arcade emulator. Great reference. 6502asm.com -- This is a JavaScript 6502 emulator with a cool little forum. dynarec'd 6502asm -- This is a little hack I did over a day or two. I took the existing emulator from 6502asm.com and changed it to dynamically recompile the code to JavaScript for massive speed increases.
处理器重新编译引用:
Michael Steil对静态重新编译的研究(如上所述)在本文中达到了顶峰,您可以在这里找到源代码等。
附录:
自从这个答案提交以来已经有一年多了,它得到了所有的关注,我认为是时候更新一些东西了。
也许现在仿真中最令人兴奋的东西是libcpu,由前面提到的Michael Steil发起。它是一个旨在支持大量CPU内核的库,这些CPU内核使用LLVM进行重新编译(静态和动态!)它有巨大的潜力,我认为它会在模拟方面做得很好。
Emu-docs也引起了我的注意,它有一个很棒的系统文档存储库,对于模拟目的非常有用。我在那里待的时间不长,但看起来他们有很多很棒的资源。
我很高兴这篇文章对你有所帮助,我希望我能在年底/明年初完成我关于这个主题的书。
关于模拟真实系统或你自己的东西的建议? 我可以说模拟器通过模拟整个硬件来工作。也许不是电路(像HW那样四处移动比特)。移动字节是最终结果,所以复制字节是好的)。模拟器是很难创建的,因为有很多黑客(如在不寻常的效果),时间问题,等等,你需要模拟。如果一个(输入)部分是错误的,那么整个系统就会崩溃,或者最多出现一个bug/小故障。
是的,你必须“手工”解释整个二进制机器码的混乱。不仅如此,大多数情况下,您还必须模拟一些在目标机器上没有等效硬件的外来硬件。
简单的方法是一个接一个地解释指令。这工作得很好,但是很慢。一种更快的方法是重新编译——将源机器码转换为目标机器码。这比较复杂,因为大多数指令都不是一对一映射的。相反,您将不得不制定涉及额外代码的详细变通方案。但最终还是要快得多。大多数现代模拟器都是这样做的。
当你开发一个模拟器时,你是在解释系统正在运行的处理器组件(Z80、8080、PS CPU等)。
您还需要模拟系统拥有的所有外设(视频输出、控制器)。
你应该开始为简单的系统编写模拟器,比如旧的Game Boy(使用Z80处理器,我是不是没有弄错)或C64。
我知道这个问题有点老了,但我想在讨论中补充一些东西。这里的大多数答案都围绕模拟器解释它们所模拟的系统的机器指令。
然而,有一个非常著名的例外,称为“UltraHLE”(维基百科文章)。UltraHLE是有史以来最著名的模拟器之一,它模拟了商用任天堂64游戏(在家用电脑上表现不错),当时人们普遍认为这是不可能的。事实上,当UltraHLE诞生时,任天堂还在为任天堂64制作新游戏!
这是我第一次在印刷杂志上看到关于模拟器的文章,而以前我只在网上看到过它们的讨论。
UltraHLE的概念是通过模拟C库调用而不是机器级调用来实现不可能。