认为这可能更适合programmers.stackexchange.com,但无论如何:
我不会说你需要任何特定的知识。了解微处理器和内存管理的工作原理以及汇编程序知识将有很大帮助(因为您实际上是在为类似汇编程序的代码编写解释器)。假设你已经知道你的目标硬件是如何工作的,那么你所要做的就是实现以下组件(即伪造真实硬件并模仿/模拟它的行为):
- 模拟 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/no operation”或作为终止符(如本例所示)。现在只需删除换行符,您最终就会得到自定义字节码。
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)。
如果您正在寻找更多要阅读的资源,我会尝试使用一些简单的处理器并开始学习汇编代码。如果可能,选择一个非常简单的架构。概念总是相同的,但随着指令集、地址空间虚拟化等越来越多,事情可能会变得复杂。