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

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

你能给那些对编写模拟器(特别是游戏系统)感兴趣的人一些建议吗?


当前回答

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

其他回答

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

这是一个很好的开始,因为系统不是很复杂,但你仍然了解操作码、堆栈、寄存器等是如何工作的。

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

我将如何开始模仿。

1.买一些关于低级编程的书籍,你将需要它来“模拟”任天堂的操作系统……游戏的男孩……

2.专门找一些模拟方面的书,也许还有操作系统开发方面的。(你不会做一个操作系统,但最接近它。

3.看看一些开源仿真器,特别是那些您想为其创建仿真器的系统。

4.将更复杂的代码片段复制到IDE/编译器中。这将节省编写长代码的时间。这就是我为操作系统开发所做的,使用linux的一个区域

添加@Cody Brocious提供的答案 在虚拟化环境中,您正在向虚拟机模拟一个新系统(CPU、I/O等),我们可以看到以下类别的模拟器。

解释:bochs是解释器的一个例子,它是一个x86 PC模拟器,它把来自客户系统的每条指令翻译成另一组指令(主机ISA的指令)来产生预期的效果。是的,它非常慢,它不缓存任何东西,所以每条指令都经过相同的周期。

动态仿真器:Qemu是一个动态仿真器。它可以实时翻译客户指令,也可以缓存结果。最好的部分是直接在主机系统上执行尽可能多的指令,这样模拟就更快了。正如Cody所提到的,它将代码划分为块(一个单独的执行流)。

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

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

基本思想:

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

处理器仿真:

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

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

使用所有这些路径,您有相同的总体目标:执行一段代码来修改处理器状态并与“硬件”交互。处理器状态是给定处理器目标的处理器寄存器、中断处理程序等的集合体。对于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也引起了我的注意,它有一个很棒的系统文档存储库,对于模拟目的非常有用。我在那里待的时间不长,但看起来他们有很多很棒的资源。

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

一个叫维克多·莫亚·德尔·巴里奥的人写了一篇关于这个主题的论文。152页里有很多有用的信息。你可以在这里下载PDF文件。

如果您不想注册scribd,您可以谷歌获取PDF标题,“模拟编程技术的研究”。PDF文档有几个不同的来源。