模拟器是如何工作的?当我看到NES/SNES或C64模拟器时,我很震惊。
您是否必须通过解释特定的组装指令来模拟这些机器的处理器?还有什么?它们通常是如何设计的?
你能给那些对编写模拟器(特别是游戏系统)感兴趣的人一些建议吗?
模拟器是如何工作的?当我看到NES/SNES或C64模拟器时,我很震惊。
您是否必须通过解释特定的组装指令来模拟这些机器的处理器?还有什么?它们通常是如何设计的?
你能给那些对编写模拟器(特别是游戏系统)感兴趣的人一些建议吗?
当前回答
关于模拟真实系统或你自己的东西的建议? 我可以说模拟器通过模拟整个硬件来工作。也许不是电路(像HW那样四处移动比特)。移动字节是最终结果,所以复制字节是好的)。模拟器是很难创建的,因为有很多黑客(如在不寻常的效果),时间问题,等等,你需要模拟。如果一个(输入)部分是错误的,那么整个系统就会崩溃,或者最多出现一个bug/小故障。
其他回答
还可以查看Darek Mihocka的Emulators.com,获得关于jit指令级优化的建议,以及许多关于构建高效模拟器的其他好东西。
仿真是一个多方面的领域。下面是基本思想和功能组件。我将把它分成几部分,然后通过编辑填充细节。我将要描述的许多东西都需要了解处理器的内部工作原理——组装知识是必要的。如果我在某些事情上有点太模糊,请提问,这样我可以继续改进这个答案。
基本思想:
仿真通过处理处理器和各个组件的行为来工作。您构建系统的每个单独部分,然后将这些部分连接起来,就像硬件中的电线一样。
处理器仿真:
有三种处理处理器仿真的方法:
解释 动态重新编译 静态重新编译
使用所有这些路径,您有相同的总体目标:执行一段代码来修改处理器状态并与“硬件”交互。处理器状态是给定处理器目标的处理器寄存器、中断处理程序等的集合体。对于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也引起了我的注意,它有一个很棒的系统文档存储库,对于模拟目的非常有用。我在那里待的时间不长,但看起来他们有很多很棒的资源。
我很高兴这篇文章对你有所帮助,我希望我能在年底/明年初完成我关于这个主题的书。
在创建了我自己的80年代BBC微型计算机模拟器(类型VBeeb到谷歌)后,有许多事情要知道。
You're not emulating the real thing as such, that would be a replica. Instead, you're emulating State. A good example is a calculator, the real thing has buttons, screen, case etc. But to emulate a calculator you only need to emulate whether buttons are up or down, which segments of LCD are on, etc. Basically, a set of numbers representing all the possible combinations of things that can change in a calculator. You only need the interface of the emulator to appear and behave like the real thing. The more convincing this is the closer the emulation is. What goes on behind the scenes can be anything you like. But, for ease of writing an emulator, there is a mental mapping that happens between the real system, i.e. chips, displays, keyboards, circuit boards, and the abstract computer code. To emulate a computer system, it's easiest to break it up into smaller chunks and emulate those chunks individually. Then string the whole lot together for the finished product. Much like a set of black boxes with inputs and outputs, which lends itself beautifully to object oriented programming. You can further subdivide these chunks to make life easier.
Practically speaking, you're generally looking to write for speed and fidelity of emulation. This is because software on the target system will (may) run more slowly than the original hardware on the source system. That may constrain the choice of programming language, compilers, target system etc. Further to that you have to circumscribe what you're prepared to emulate, for example its not necessary to emulate the voltage state of transistors in a microprocessor, but its probably necessary to emulate the state of the register set of the microprocessor. Generally speaking the smaller the level of detail of emulation, the more fidelity you'll get to the original system. Finally, information for older systems may be incomplete or non-existent. So getting hold of original equipment is essential, or at least prising apart another good emulator that someone else has written!
当你开发一个模拟器时,你是在解释系统正在运行的处理器组件(Z80、8080、PS CPU等)。
您还需要模拟系统拥有的所有外设(视频输出、控制器)。
你应该开始为简单的系统编写模拟器,比如旧的Game Boy(使用Z80处理器,我是不是没有弄错)或C64。
一个叫维克多·莫亚·德尔·巴里奥的人写了一篇关于这个主题的论文。152页里有很多有用的信息。你可以在这里下载PDF文件。
如果您不想注册scribd,您可以谷歌获取PDF标题,“模拟编程技术的研究”。PDF文档有几个不同的来源。