有时会想到一个很难摆脱的想法。 这发生在我身上。
考虑到当时我还没有想法,所以我决定创建一个虚拟机(VM),在我看来这是个好主意。 如果您有兴趣,那就去切!
理论
首先,一点理论。 一般而言,什么是虚拟机? 这是一个程序或一组程序,可让您模拟某种硬件平台,即计算机模拟器。
虚拟机本身是不同的,例如,Virtual Box是允许您模拟真实计算机的经典虚拟机,但是JVM(Java虚拟机)无法做到这一点。
我的VM版本与JVM有点相似,只是因为它比旨在创建功能强大的VM的培训项目更多。
记忆
因此,现在让我们找出内存。 为了创建内存,我决定使用无符号的int数组。 数组的大小是使用宏确定的,在我的版本中,内存大小为4096字节(数组中有1024个元素,并且由于在大多数平台上为无符号int数据分配了4个字节,因此1024 * 4 = 4096),除其他外,我们将定义8个寄存器每个8个单元将已经是256个字节(8 * 8 * 4 = 256)。 看起来像这样:
#define MEMSIZE 1024 unsigned int memory[MEMSIZE]; unsigned int reg[8][8];
程式设计
我们有内存,但是现在如何为我们的VM编写代码? 现在,我们将解决这个问题,首先,我们将确定计算机将执行的命令:
enum commands { CRG = 1, CRC, PRG, PRC };
每个小组都有自己的标志,用于定义一些其他参数。
我们将描述标志:
enum flags { STDI = 1, STDA };
标准命令的形式为:[命令] [标志] [数据](某些命令的外观可能有所不同),基于此我们将编写一个简单的解释器:
if (memory[cell] == CRG && memory[cell + 1] == STDI) { indxX = memory[cell + 2]; cell++; } else if (memory[cell] == CRC && memory[cell + 1] == STDI) { indxY = memory[cell + 2]; cell++; } else if (memory[cell] == PRG && memory[cell + 1] == STDI) { reg[indxX][0] = memory[cell + 2]; cell++; } else if (memory[cell] == PRC && memory[cell + 1] == STDI) { reg[indxX][indxY] = memory[cell + 2]; cell++; }
indxX和indxY是将当前光标位置存储在reg寄存器中的变量。
Cell是一个变量,用于将当前光标位置存储在内存阵列中。
但是用数字编程不是很方便,因此使用C预处理器将描述汇编程序。 我知道用宏编写asm并不是很好,但是这种解决方案是暂时的。
我们的asm代码如下所示:
#define $CRG {memory[memIndx++] = CRG;} #define $CRC {memory[memIndx++] = CRC;} #define $PRG {memory[memIndx++] = PRG;} #define $PRC {memory[memIndx++] = PRC;} #define _$STDI {memory[memIndx++] = STDI;} #define _$STDA {memory[memIndx++] = STDA;} #define _$DATA memory[memIndx++] =
memIndx是一个变量,用于将当前光标位置存储在内存阵列中。
这是我们的汇编代码,将123放入地址[1] [0]的寄存器中(第一个寄存器,零单元):
$CRG _$STDI _$DATA 1; $CRC _$STDI _$DATA 0; $PRC _$STDI _$DATA 123;
恭喜,我们现在为我们的汽车提供了类似asm的外观!
启动程序
我们设法使我们的机器执行程序,但是代码缺乏从一台机器到另一台机器的可移植性,所以现在我们要从asm创建机器代码生成器(而且我要提醒您,与真实计算机不同,我们的机器具有不以二进制形式显示的机器代码,和十进制数),原则上并没有那么困难,但是首先让我们考虑一下实现。
首先,我们有asm代码,现在我们需要将其转换为数字,然后将生成的机器代码写入.ncp文件(数字代码程序,实际上是文本文件,但为了与其他文件区分开,我想出了自己的扩展名),之后我们需要运行.ncp文件,这很简单,因为我们之前编写的解释器可以识别数字,因此我们只需要从文件中提取数据,然后使用atoi()将其转换为数字即可。
让我们从言传身行:
读取代码并将其写入文件:
if (memory[i] == CRG && memory[i + 1] == STDI) { fprintf(code, "%d %d ", CRG, STDI); i++; } else if (memory[i] == CRC && memory[i + 1] == STDI) { fprintf(code, "%d %d ", CRC, STDI); i++; } else if (memory[i] == PRG && memory[i + 1] == STDI) { fprintf(code, "%d %d ", PRG, STDI); i++; } else if (memory[i] == PRC && memory[i + 1] == STDI) { fprintf(code, "%d %d ", PRC, STDI); i++; }
该代码是ncpGen()函数主体的一部分。
读取文件及其执行:
if (prog != NULL) { fread(txt, 1, len, prog); tok = strtok(txt, " "); while (tok != NULL) { memory[i] = atoi(tok); tok = strtok(NULL, " "); if (argc == 3 && strcmp(argv[2], "-m") == 0) { printf("%d\n", memory[i]); } i++; } memInter(); } else { perror("Fail"); }
现在,让我们定义一个宏,以便代替解释asm,代码变成.ncp:
#define _toNCP(name) {strcpy(filename, name);} {ncpGen();}
如果有的话,本文将不介绍所有代码,而只是其中的一小部分!
完整的代码位于项目
存储库中 。
非常感谢您的阅读!