如何学习编写游戏主机模拟器(特别是硬件知识)?

3
我之前问过一个类似的问题,关于如何编写模拟器,当我看到答案并查找了一些开源的Gameboy模拟器时,我发现需要的远不止对“低级”语言(比如C)有中级理解。实际上,我想知道的是,为了能够编写这样的模拟器,我需要了解硬件方面的知识。我在Java方面已经接近“相当不错”的水平,但这对我帮助不大。我已经开始学习C,并且取得了很大进展,但我想知道我需要学习哪些硬件知识才能编写这样的控制台模拟器。
我提出这个问题的原因是,我在互联网上搜索了一些关于模拟器的书籍和资料,每当我遇到一个模拟器教程时,总是说“你需要基本的C语言理解才能跟随本教程”,但当我尝试跟随时,我却无法理解,因为我根本不知道在编写什么。我对硬件一窍不通(我爸爸连怎么把显示器连接到电脑上都费劲,他可能比我更懂)。
所以我的问题是,我需要学习什么(微处理器、内存等等)?有没有一些好的资源可以教我这些东西,而不需要我去上大学之类的(我已经在学软件工程了)?
如果这个问题不是真实的问题,或者不够清楚,或者其他什么的,我向你道歉。我只是对这个主题非常感兴趣。

2
参见:https://dev59.com/ZHRB5IYBdhLWcg3w-8A9 - Mysticial
我已经阅读了这篇文章,虽然其中包含了相当多有用的信息,但唯一明确指出需要哪些硬件知识的是“微处理器”。我假设一个游戏机不仅仅由一个微处理器组成?学习如何编写模拟器只需要掌握这个吗?(我知道这听起来很愚蠢,但请耐心听我说) - ZimZim
基本上,你需要了解关于模拟设备的编程方面的一切:你需要了解它的汇编语言,需要模拟的所有外围设备的硬件接口,特别是其显示子系统的所有原始细节,以及如果有操作系统的话,也需要了解其工作原理!如果你发现逐条解释指令不够高效,那么对编译器构建的良好基础也将非常有用。 - hmakholm left over Monica
4个回答

12

我认为这个问题可能更适合程序员交流网站,但无论如何:

我不认为你需要任何特定的知识。了解微处理器和内存管理的工作原理将会有很大帮助,以及汇编知识(因为你实际上是在编写汇编代码的解释器)。假设你已经知道目标硬件的工作原理,你所要做的就是实现以下组件(即模拟真实硬件并模仿/仿真其行为):

  • 模拟CPU解析和运行机器码。
  • 模拟输入组件(将键盘、鼠标或操纵杆等输入转换为可供CPU访问的“引脚信号”)。
  • 模拟输出组件(将图像和音频数据转换为可见的东西,例如在屏幕上显示某些内容或生成类似于真实硬件组件的声音)。
  • 模拟其他组件,如存储卡、内置电池、插卡、扩展芯片等。

作为一般建议,我建议暂时不要考虑编写任何模拟器。相反,从尝试编写自己的字节码解释器开始,做一些简单的事情。

例如,你可以使用一个简单的伪代码序列:

x = 5
y = 10
print(x + y)

使用伪汇编语言编写的话,代码可能如下所示:

mov x, 5
mov y, 10
add x, y // to be honest, this is essentially x += y; not just x + y
prt x
end

现在用(代码)数字替换所有指令。所有变量都被数字替换:
0x01 0x00 0x05
0x01 0x01 0x0a
0x02 0x00 0x01
0x03 0x00
0x00

这可能看起来有点奇怪,但我保留了上面的顺序和换行符。请注意,我在第一个指令中使用了0x01而不是0x00。这只是为了方便,这样我可以使用0x00作为“NOOP/无操作”或终止符(在本例中)。现在只需删除换行符,你就得到了自定义的字节码。

char code[] = {0x01, 0x00, 0x05, 0x01, 0x01, 0x0a, 0x02, 0x00, 0x01, 0x03, 0x00, 0x00};

要运行这段代码,您可以编写一个简单的“模拟器”。它只有一个有限的指令集,但它会起作用。实际上我没有测试这段代码,所以请记住我可能在某些地方搞砸了。然而,这应该足以让你了解如何做什么。请注意,在这个例子中,我没有分离数据内存和指令内存。这会增加更多的复杂性,现在并不是真正需要的。
int pos = 0; // essentially being our "instruction pointer"
char registers[10];
while(code[pos]) { // still some instruction
    switch(code[pos++]) { // determine what to do and move the instruction pointer
    // 0x00 isn't handled here, as that's part of our loop already
    case 0x01: // mov(e)
        registers[code[pos++]] = code[pos++]; // read the value into the register
        break;
    case 0x02: // add
        registers[code[pos++]] += registers[code[pos++]];
        break;
    case 0x03: // prn
        printf("%d", registers[code[pos++]]);
        break;
    }
}

一旦你理解了我在这里所做的,我相信你应该会更容易理解其他来源。哦,还有一个有趣的事实:如果你只是为了教育目的而不是编写高效的模拟器,你可以开始使用Java。它可能不够优化和慢,但你基本上可以做同样的事情,可能甚至消除你前进路上的障碍。

只是一个快速的提醒:在上面的示例中,你可以有第二个线程不断地将registers[]的内容写到屏幕上。这样你就可以基本上模拟一些视频硬件(例如电视屏幕或LED)。

如果你正在寻找更多阅读材料,我建议你选择一些简单的处理器并开始学习汇编代码。如果可能的话,请选择一个非常简单的架构。概念始终相同,但随着越来越多的指令集、地址空间虚拟化等问题变得越来越复杂。


1
谢谢你,我爱你,你是一个绝对的英雄,花时间向我详细解释这个问题:D - ZimZim

1

我建议学习汇编语言来获取你在这里所需的基本知识。如果你对超级任天堂游戏感兴趣,可以尝试学习65816汇编语言,这是该控制台使用的处理器的汇编语言。要开始学习,可以从这里开始阅读:http://en.wikibooks.org/wiki/Super_NES_Programming。其他控制台也有类似的资源。我还建议使用具有调试功能的模拟器,例如Geiger的debugging snes9x,它将让您逐步执行游戏、转储其正在执行的操作、检查内存等。虽然这些内容与编写控制台模拟器没有直接关系,但它会为您提供一些背景知识。

不过,说实话,如果C语言对您来说完全陌生,最好先学习一些C语言再尝试学习汇编语言。C语言比汇编语言抽象得多,但仍然会为您提供许多重要概念的基础,最重要的是指针。

基本上,模拟器的作用是逐条执行游戏的机器码。控制台是一个相当复杂的设备,具有多个处理器、协处理器和相关硬件协同工作。因此,您应该从一个更简单的模拟器开始,它仅模拟CPU,并忽略其他部分。学习汇编语言之所以对此非常有用,是因为汇编语言与模拟器应处理的机器码具有1:1的对应关系。

1

如果你说你对硬件一无所知,那么编写模拟器并不容易。了解硬件的基本功能可能比了解任何特定的编程语言更重要。

我可以推荐Noam Nisan和Shimon Schocken的书《计算系统的元素》及其相关的互联网资源(http://www.nand2tetris.org/)。该书从简单的nand门开始,逐步构建一个功能完备的计算机,然后编写汇编器、编译器、操作系统,最后是俄罗斯方块游戏。在每个级别上,您可以“自己构建”或使用网络上可用的答案来构建下一个级别。


0
首先,编写模拟器需要多个领域的知识。你需要牢固掌握计算机体系结构和所选编程语言这两个大领域。如果你没有任何编程经验,那么创建一个模拟器不是一个好主意。话虽如此,只要你熟练掌握某种编程语言,你就可以使用它来创建模拟器!
为了理解系统的工作原理,建议你更新一下计算机体系结构的知识。我推荐阅读一本关于该主题的好书,比如Computer Architecture: A Quantitative Approach,这是大多数体系结构课程普遍使用的一本书。
一旦你具备了基本的系统操作知识(CPU、内存、寄存器、指令集等),你就可以尝试模拟你所选择的系统。在这个阶段,我建议你尽可能地搜集有关你所选择的系统的信息。Zophar.net上有很多关于各种游戏机的技术文档,非常实用。

一旦您对想要模拟的系统有了工作知识,我建议编写代码来读取ROM文件并提取文件内容(特别是指令)。下一步是模拟体系结构的CPU并为每个指令编写处理程序。我通过运行从ROM加载的实际指令来测试CPU。在您实现了足够数量的指令之后,可以开始实现其他子系统,例如图形和事件/中断系统。我通常实现的最终系统是声音。

(无耻的宣传)我正在撰写一本名为逐步模拟的书,其中更详细地介绍了所有这些内容,希望在几个月内出版。我还没有看到其他关于这个主题的书籍,这真是可耻,因为这是一个如此有趣的话题!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接