我认为这个问题可能更适合程序员交流网站,但无论如何:
我不认为你需要任何特定的知识。了解微处理器和内存管理的工作原理将会有很大帮助,以及汇编知识(因为你实际上是在编写汇编代码的解释器)。假设你已经知道目标硬件的工作原理,你所要做的就是实现以下组件(即模拟真实硬件并模仿/仿真其行为):
- 模拟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;
char registers[10];
while(code[pos]) {
switch(code[pos++]) {
case 0x01:
registers[code[pos++]] = code[pos++];
break;
case 0x02:
registers[code[pos++]] += registers[code[pos++]];
break;
case 0x03:
printf("%d", registers[code[pos++]]);
break;
}
}
一旦你理解了我在这里所做的,我相信你应该会更容易理解其他来源。哦,还有一个有趣的事实:如果你只是为了教育目的而不是编写高效的模拟器,你可以开始使用Java。它可能不够优化和慢,但你基本上可以做同样的事情,可能甚至消除你前进路上的障碍。
只是一个快速的提醒:在上面的示例中,你可以有第二个线程不断地将registers[]
的内容写到屏幕上。这样你就可以基本上模拟一些视频硬件(例如电视屏幕或LED)。
如果你正在寻找更多阅读材料,我建议你选择一些简单的处理器并开始学习汇编代码。如果可能的话,请选择一个非常简单的架构。概念始终相同,但随着越来越多的指令集、地址空间虚拟化等问题变得越来越复杂。