emulation - 模拟器如何工作以及它们是如何编写的?

标签 emulation c64

关闭。这个问题是off-topic .它目前不接受答案。




8年前关闭。










锁定。这个问题及其答案是locked因为这个问题是题外话,但具有历史意义。它目前不接受新的答案或互动。








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

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

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

对于有兴趣编写模拟器(尤其是游戏系统)的人,您能提供什么建议吗?

最佳答案

仿真是一个多方面的领域。以下是基本思想和功能组件。我将把它分成几部分,然后通过编辑填写细节。我将要描述的许多事情都需要了解处理器的内部工作原理——汇编知识是必要的。如果我对某些事情有点过于含糊,请提出问题,以便我继续改进此答案。
基本思路:
仿真通过处理处理器和各个组件的行为来工作。您构建系统的每个单独部分,然后像硬件中的电线一样连接这些部分。
处理器仿真:
处理处理器仿真的方式有以下三种:

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

  • 对于所有这些路径,您具有相同的总体目标:执行一段代码来修改处理器状态并与“硬件”交互。处理器状态是给定处理器目标的处理器寄存器、中断处理程序等的集合。对于 6502,您将有许多表示寄存器的 8 位整数:A , X , Y , P , 和 S ;你还有一个 16 位 PC登记。
    有了口译,您从 IP 开始(指令指针 - 也称为 PC ,程序计数器)并从内存中读取指令。您的代码解析此指令并使用此信息来更改处理器指定的处理器状态。解释的核心问题是它非常慢。每次处理给定指令时,都必须对其进行解码并执行必要的操作。
    使用动态重新编译,您可以像解释一样迭代代码,但不仅仅是执行操作码,您还可以构建一个操作列表。一旦到达分支指令,就将这个操作列表编译为主机平台的机器代码,然后缓存这个编译后的代码并执行它。然后当你再次命中给定的指令组时,你只需要从缓存中执行代码。 (顺便说一句,大多数人实际上并没有列出指令列表,而是将它们即时编译为机器代码——这使得优化变得更加困难,但这超出了本答案的范围,除非有足够多的人感兴趣)
    使用静态重新编译,您可以执行与动态重新编译相同的操作,但您遵循分支。您最终构建了一个代表程序中所有代码的代码块,然后可以在没有进一步干扰的情况下执行这些代码。如果不是因为以下问题,这将是一个很好的机制:
  • 不在程序中的代码(例如,在运行时压缩、加密、生成/修改等)不会被重新编译,因此它不会运行
  • 已经证明,找到给定二进制文件中的所有代码等同于 Halting problem

  • 这些结合起来使静态重新编译在 99% 的情况下完全不可行。有关更多信息,Michael Steil 对静态重新编译做了一些很棒的研究——这是我见过的最好的。
    处理器仿真的另一面是您与硬件交互的方式。这真的有两个方面:
  • 处理器时序
  • 中断处理

  • 处理器时序:
    某些平台——尤其是像 NES、SNES 等较旧的控制台——需要你的模拟器有严格的时间来完全兼容。使用 NES,您拥有 PPU(像素处理单元),它要求 CPU 在精确的时刻将像素放入其内存中。如果您使用解释,您可以轻松计算周期并模拟正确的计时;使用动态/静态重新编译,事情变得/很多/更复杂。
    中断处理:
    中断是 CPU 与硬件通信的主要机制。通常,您的硬件组件会告诉 CPU 它关心什么中断。这非常简单——当你的代码抛出一个给定的中断时,你查看中断处理程序表并调用正确的回调。
    硬件仿真:
    模拟给定的硬件设备有两个方面:
  • 模拟设备的功能
  • 模拟实际设备接口(interface)

  • 以硬盘驱动器为例。通过创建后备存储、读/写/格式化例程等来模拟该功能。这部分通常非常简单。
    设备的实际界面要复杂一些。这通常是内存映射寄存器(例如,设备监视更改以执行信令的部分内存)和中断的某种组合。对于硬盘驱动器,您可能有一个内存映射区域,您可以在其中放置读取命令、写入等,然后读回这些数据。
    我会更详细地介绍,但是您可以使用一百万种方法。如果您在此处有任何具体问题,请随时提问,我会添加信息。
    资源:
    我想我在这里给出了一个很好的介绍,但还有一个 的额外区域。我很乐意帮助解决任何问题;由于极其复杂,我对其中的大部分内容都非常含糊。
    强制性维基百科链接:
  • Emulator
  • Dynamic recompilation

  • 一般仿真资源:
  • Zophar -- 这就是我开始进行仿真的地方,首先下载仿真器,并最终掠夺他们庞大的文档文件。这是您可能拥有的绝对最佳资源。
  • NGEmu -- 没有多少直接资源,但他们的论坛是无与伦比的。
  • RomHacking.net -- 文档部分包含有关流行控制台机器架构的资源

  • 模拟器项目引用:
  • IronBabel -- 这是 .NET 的仿真平台,用 Nemerle 编写,可即时将代码重新编译为 C#。免责声明:这是我的项目,所以请原谅无耻的插件。
  • BSnes -- 一个很棒的 SNES 模拟器,其目标是循环完美的准确性。
  • MAME -- 街机模拟器。很好的引用。
  • 6502asm.com -- 这是一个带有很酷的小论坛的 JavaScript 6502 模拟器。
  • dynarec'd 6502asm -- 这是我在一两天内做的一个小技巧。我从 6502asm.com 获取了现有的模拟器并将其更改为动态地将代码重新编译为 JavaScript,以大幅提高速度。

  • 处理器重编译引用:
  • Michael Steil(引用上文)对静态重新编译的研究在 this paper 中达到了高潮。你可以找到源和这样的here .

  • 附录:
    自从提交这个答案已经一年多了,随着它受到的关注,我想是时候更新一些东西了。
    也许现在仿真中最令人兴奋的事情是 libcpu ,由前面提到的 Michael Steil 开始。它是一个旨在支持大量 CPU 内核的库,这些内核使用 LLVM 进行重新编译(静态和动态!)。它具有巨大的潜力,我认为它会为仿真做出巨大贡献。
    emu-docs也引起了我的注意,它包含一个很好的系统文档存储库,对于仿真目的非常有用。我在那里待的时间不多,但看起来他们有很多很棒的资源。
    我很高兴这篇文章很有帮助,我希望我能在今年年底/明年年初之前完成我关于这个主题的书。

    关于emulation - 模拟器如何工作以及它们是如何编写的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/448673/

    相关文章:

    java - 如何克服 LWUIT 中的 RecordStoreFullException 错误?

    android-studio - Android Studio 中的“编辑配置”中没有“模拟器”选项卡

    android - 如何在android虚拟设备中安装oculus sdk?

    ubuntu - Bochs:显示库 'sdl' 不可用

    basic - 如何在 commodore 64 basic 中分隔字符串?

    assembly - 我可以通过一些 16 位魔法将其变成循环吗?

    emulation - 6502 CPU 仿真

    assembly - 修改了 C64 PRG BASIC header ?

    retro-computing - 在 Commodore 64 中绘制屏幕边框