目录
0x01 背景
《C语言大学教程-第八版》(《C How to Program》)246页,第七章作业,专题:构建自己的计算机
打开一台计算机来看看它的内部结构。我们要介绍机器语言程序设计并编写几个机器语言程序。为了使我们的经历更加有价值,将构建一台计算机(当然是基于软件模拟的技术)并在上面执行自己的机器语言程序。
0x02 要求
《C语言大学教程-第八版》(《C How to Program》)第七章作业,7.27,7.28,7.29(246~250页)
内存
Simpletron具有100个字的内存,而这些字用它们的位置编号00,01,…,99来引用。在运行SML程序之前,我们必须将程序加载到内存中。每个SML程序的第1条指令(或者语句)总是安排在位置00。用SML编写的每条指令将占据 Simpletron内存中的1个字(因此指令是有符号的4位十进制数字)。我们假设SML命令的符号总是加号,但数据字的符号可能是加号或者减号。Simpletron内存中的每个位置可能包含一条指令,程序所使用的数据值,或者内存的未使用区域(因此没有定义)。每条SML语句的前2位是操作码,它规定了要执行的操作。
寄存器与指令结构
用一个名为accumulator
的变量来表示累加器。用变量instructionCounter
来记录正在执行的指令的存储地址。为了指示当前正在执行的操作——即指令字中的左边两位数字,我们引入了变量operationCode
。用变量operand
来指示当前指令所处理的内存地址。因此,如果指令有operand
的话,operand
就是指令中最右边两位数字。指令并不是直接从主存中取出后就被执行的。相反,即将被执行的下一条指令从内存中被取出后首先被转存到变量instructionRegister
中,然后指令字被一分为二,左边两位数字和右边两位数字被分别存人变量operationCode
和operand
中。
操作码
0x03 应用
需求
输入两个值,比较两个值的大小,输出大值
实现
操作码文件sml.txt:
+1009 // 输入一个字并存储到09内存
+1010 // 输入一个字并存储到10内存
+2009 // 将09内存的值加载到累加器中
+3110 // 将累加器中的值减去10内存中的值,结果保存在累加器中
+4107 // 如果累加器中的值为负转移到07内存执行
+1109 // 将09内存的值输出到终端
+4300 // 退出程序
+1110 // 将10内存的值输出到终端
+4300 // 退出程序
+0000 // 无操作
+0000 // 无操作
-9999 // 程序结束
执行结果:
0x04 SML_V实现
simple_vm.h
#pragma once
/*
计算机模拟器
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define SIZE 100
#define FLAG -9999
// 输入/输出操作
// 从终端将一个字读到内存指定单元中
#define READ 10
// 将内存指定单元中的一个字写到终端
#define WRITE 11
// 回车换行
#define CRLF 12
// 输入字符串
#define INSTRING 13
// 输出字符串
#define OUTSTRING 14
// 载入/存储操作
// 将指定单元中的一个字载入累加器
#define LOAD 20
// 从累加器将一个字存放回内存指定单元中
#define STORE 21
// 算术运算操作
// 将内存中指定单元中的一个字域累加器中的字相加,结果留在累加器中
#define ADD 30
// 将累加器中的字减去内存指定单元中的一个字,结果留在累加器中
#define SUBTRACT 31
// 将累加器中的字除以内存指定单元中的一个字,结果留在累加器中
#define DIVIDE 32
// 将累加器中的字乘以内存指定单元中的一个字,结果留在累加器中
#define MUTIPLY 33
// 求模,结果放在累加器中
#define MODE 34
// 求幂,结果放在累加器中
#define POWER 35
// 控制操作的转移
// 转移到内存指定单元
#define BRANCH 40
// 如果累加器为负数,则转移到内存指定单元
#define BRANCHNGE 41
// 如果累加器为零,则转移到内存指定单元
#define BRANCHZERO 42
// 停机,即程序完成了它的任务
#define HALT 43
// map文件句柄
FILE* map_file_ptr;
// map文件名
#define MAP_FILE_NAME "map_file.txt"
//用有100元素的一维数组 memory 来模拟 Simpletron 的内存
int memory[SIZE] = { 0 };
// 使用变量 accumulator来表示累加器寄存器
int accumulator = 0;
// 使用变量 instructionCounter来跟踪包含正在执行的指令的内存位置
int instructionCounter = 0;
// 使用变量 operationCode来说明当前正在执行的操作, 也就是命令字的左边2位
int operation_code = 0;
// 使用变量 operand来指出当前指令所操作的内存位置,命令的右两位
int operand = 0;
// 不要直接从内存执行指令。而是将要执行的下一条指令从内存转移到变量 instructionRegister中
// 然后“取掉”左边的2位,并将它们放置在变量 operationCode中,“取掉”右边的2位,并将它们放置在 Operand中。
int instruction_register = 0;
// 指令计数器
int num = 0;
// debug flag debug模式默认关闭
int debug = 0;
// 下一条指令
void next_instruction();
// 执行指令
int do_instruction();
// 打印内存
void dump();
// 读取 sml指令
int read_file(int n, char* file_name);
// 打印程序头
void print_head();
// 打印到 map文件中
void print_to_map(char* message, int code);
// 除零错误
int err_zero(int operand);
// 累加器溢出
int err_accumulator(int accumulator);
// 操作码错误
int err_operand_code(int operation_code);
// 换行
void new_line();
// 输入字符串
void in_str();
// 输出字符串
void out_str();
simple_vm.c
#include "simple_vm.h"
// ---------------------------------------------string
char* s1 = "****************************\n";
char* s2 = "*** 欢迎使用 Simpletron! ***\n";
char* s3 = "*** 请输入一条指令或数据 ***\n";
char* s4 = "*** 在指令或数据前会打上位置和‘?’***\n";
char* s5 = "*** 然后再该位置输入对应的一个‘world’ ***\n";
char* s6 = "*** 当输入哨兵值为 -9999 时,停止输入 ***\n";
char* s7 = "*** your program. ***\n";
char* s27 = "****************************\n";
char* s8 = "*** 程序运行结束 ***\n";
char* s9 = "REGISTERS:\n";
char* s10 = "accumulator:%d\n";
char* s11 = "instructionCounter:%d\n";
char* s12 = "instructionRegister:%d\n";
char* s13 = "operationCode:%d\n";
char* s14 = "operand:%d\n";
char* s15 = "MEMORY:\n";
char* s16 = "请输入-9999~9999之间的整数:";
char* s17 = "*** Attempt to divide by zero***";
char* s18 = "***Simpletron execution abnorma11y terminated***";
char* s19 = "比较两个数的大小,返回较大的数\n";
char* s20 = "*****操作码错误*****\n*****程序终止运行*****\n";
char* s21 = "********程序加载完成********\n";
char* s22 = "********程序开始运行********\n";
char* s23 = "********累加器溢出错误*******\n********程序即将终止执行********\n";
char* s24 = "********被除数不能为0*******\n********程序即将终止执行********\n";
char* s25 = "********输入指令错误,请重新输入*******\n";
char* s26 = "********程序执行完成*******\n";
char* s28 = "********程序开始加载*******\n";
// ---------------------------------------------string
// 下一条指令
void next_instruction() {
// 当前指令为退出程序,直接退出
if (operation_code == HALT)
{
return ;
}
// 内存中的指令
instruction_register = memory[instructionCounter];
// 指令高两位
operation_code = instruction_register / 100;
// 指令低两位
operand = instruction_register % 100;
if (operation_code == -99 && operand == -99)
{
operation_code = HALT;
printf(s26);
print_to_map(s26, -1);
}
if (err_operand_code(operation_code) == 0) {
operation_code = HALT;
printf(s20);
print_to_map(s20, -1);
}
}
// 执行指令
int do_instruction() {
int counterInc = 0;
switch (operation_code)
{
case READ:
printf("请输入:?");
scanf("%d", &memory[operand]);
// 计数器+1
counterInc = 1;
break;
case WRITE:
printf("%d\n", memory[operand]);
counterInc = 1;
break;
case CRLF:
printf("\n");
counterInc = 1;
break;
case INSTRING:
in_str();
counterInc = 1;
break;
case OUTSTRING:
out_str();
counterInc = 1;
break;
case LOAD:
accumulator = memory[operand];
counterInc = 1;
break;
case STORE:
memory[operand] = accumulator;
counterInc = 1;
break;
case ADD:
accumulator += memory[operand];
if (err_accumulator(accumulator) == 0)
return 0;
counterInc = 1;
break;
case SUBTRACT:
accumulator -= memory[operand];
if (err_accumulator(accumulator) == 0)
return 0;
counterInc = 1;
break;
case DIVIDE:
if (err_zero(memory[operand]) == 0)
return 0;
accumulator /= memory[operand];
if (err_accumulator(accumulator) == 0)
return 0;
counterInc = 1;
break;
case MUTIPLY:
accumulator *= memory[operand];
if (err_accumulator(accumulator) == 0)
return 0;
counterInc = 1;
break;
case MODE:
accumulator %= memory[operand];
if (err_accumulator(accumulator) == 0)
return 0;
counterInc = 1;
break;
case POWER:
accumulator = (int)pow(accumulator, memory[operand]);
if (err_accumulator(accumulator) == 0)
return 0;
counterInc = 1;
break;
case BRANCH:
instructionCounter = operand;
break;
case BRANCHNGE:
if (accumulator < 0) instructionCounter = operand;
else counterInc = 1;
break;
case BRANCHZERO:
if (accumulator == 0) instructionCounter = operand;
else counterInc = 1;
break;
case HALT:
printf("%s", s8);
counterInc = 1;
return 1;
default:
printf(s24);
print_to_map(s24, -1);
break;
}
if (counterInc)
{
instructionCounter++;
}
// dump();
return 1;
}
void dump()
{
new_line();
printf(s9);
print_to_map(s9, -1);
printf(s10, accumulator);
print_to_map(s10, accumulator);
printf(s11, instructionCounter);
print_to_map(s11, instructionCounter);
printf(s12, instruction_register);
print_to_map(s12, instruction_register);
printf(s13, operation_code);
print_to_map(s13, operation_code);
printf(s14, operand);
print_to_map(s14, operand);
printf(s15);
print_to_map(s15, -1);
printf(" \t0\t1\t2\t3\t4\t5\t6\t7\t8\t9\n");
print_to_map(" \t0\t1\t2\t3\t4\t5\t6\t7\t8\t9\n", -1);
for (int i = 0; i < 10; i++)
{
printf("%d0\t", i);
print_to_map("%d0\t", i);
for (int j = 0; j < 10; j++)
{
printf("%d\t", memory[i * 10 + j]);
print_to_map("%d\t", memory[i * 10 + j]);
}
printf("\n");
print_to_map("\n", -1);
}
}
// 打印程序头
void print_head()
{
// 打印在命令行
//printf("%s", s1);
//printf("%s", s2);
//printf("%s", s3);
//printf("%s", s4);
//printf("%s", s5);
//printf("%s", s6);
//printf("%s", s7);
//printf("%s", s27);
print_to_map("**********************************************\n"
"*** 欢迎来到 Simpletron ! ***\n"
"*** 请每次输入一条指令或一条数据 ***\n"
"*** 在指令或数据前将会打上位置编号和‘?’ ***\n"
"*** 然后在该位置输入对应的一个‘word’ ***\n"
"*** 当输入哨兵值 - 9999 时,停止输入 ***\n"
"*** your program. ***\n"
"**********************************************\n\n", -1);
}
void print_to_map(char* message, int code) {
// 判断 map文件句柄是否为空
if (map_file_ptr == NULL)
{
map_file_ptr = fopen(MAP_FILE_NAME, "w");
}
// 再次判断 map文件句柄是否为空
if (map_file_ptr == NULL)
{
printf("打开文件失败\n");
return;
}
// 将数据写入到文件中
if (code != -1) {
fprintf(map_file_ptr, message, code);
}
else
{
fprintf(map_file_ptr, message);
}
}
// 读取 sml指令文件
int read_file(int n, char* file_name) {
// 源文件句柄
FILE* fptr;
// 指令缓冲区
char code[6] = { 0 };
// 读取文件
if ((fptr = fopen(file_name, "r")) == NULL)
printf("打开文件失败!\n");
else
{
// 从给定的 whence 位置查找的字节数
// SEEK_SET:从文件头开始
// 为什么是7个字符:1符号,4数字,1回车符+按键回车
fseek(fptr, (n++) * 7, SEEK_SET);
// 读取字符
fscanf(fptr, "%s", &code);
// 判断文件有没有读完
if (!feof(fptr))
// 字符转整数
return atoi(code);;
// 关闭文件
fclose(fptr);
}
// 指令读取完成
return -1;
}
// 除零错误
int err_zero(int operand) {
if (operand == 0)
{
printf(s24);
operation_code = HALT;
// 被除数为0,返回 false
return 0;
}
// 正常返回计算
return 1;
}
// 累加器溢出
int err_accumulator(int accumulator)
{
if (accumulator > 9999 || accumulator < -9999)
{
printf(s23);
operation_code = HALT;
// 累加器边界溢出,返回 false
return 0;
}
// 累加器正常,返回 true
return 1;
}
// 指令错误
int err_operand_code(int operationCode)
{
if (operationCode == 10 ||
operationCode == 11 ||
operationCode == 20 ||
operationCode == 21 ||
operationCode == 30 ||
operationCode == 31 ||
operationCode == 32 ||
operationCode == 33 ||
operationCode == 34 ||
operationCode == 35 ||
operationCode == 40 ||
operationCode == 41 ||
operationCode == 42 ||
operationCode == 43) {
return 1;
}
return 0;
}
// 换行
void new_line() {
printf("\n\n");
print_to_map("\n\n", -1);
}
// 输入字符串
void in_str() {
// 字数计数器
int i = 0;
// 字符串内存
char* strMemory = (char*)memory[operand];
// +1,下一个地址存放字符
strMemory++;
// 清空缓冲区'\n'
getchar();
while ((*strMemory = getchar()) != '\n')
{
// 计数器+1
i++;
// 内存地址+1
strMemory++;
}
// 第一个字节保存字符串的长度
*(strMemory - i - 1) = i;
}
// 输出字符串
void out_str() {
// 字符串首地址
char* offset = (char*)&memory[operand];
// 字符串长度
int count = *offset;
// 字符串正文首地址
offset++;
for (int i = 0; i < count; i++, offset++)
printf("%c", *offset);
}
// main函数
int main()
{
// 打印程序头
print_head();
// 打印内存
// dump();
// 换行
new_line();
printf(s28);
print_to_map(s28, -1);
char smlFname[30];
printf("SML文件名:");
gets_s(smlFname, 30);
int line = 0;// 读取行数计数器
// 程序加载到内存
while (1)
{
// 打印内存编号
// printf("%d ? +\n", instructionCounter);
print_to_map("%d ? +\n", instructionCounter);
while (1)
{
// 读取文件指令,加载到内存中
if ((memory[instructionCounter] = read_file(num, smlFname)) != -1)
num++;
// 输入的指令检查
if (memory[instructionCounter] <= 9999 && memory[instructionCounter] >= -9999)
break;
else
printf("输入错误,请重新输入\n");
}
// 内存计数器+1
instructionCounter++;
// 指令输入完成标识
if (memory[instructionCounter - 1] == FLAG)
break;
}
// 日志输出
printf(s21);
print_to_map(s21, -1);
// dump();
printf(s22);
print_to_map(s22, -1);
// 开始执行程序
instructionCounter = 0;
// 执行程序
while (1)
{
if (operation_code == 43)
{
break;
}
next_instruction();
if (!do_instruction())
{
break;
}
}
dump();
return EXIT_SUCCESS;
}
0x05 总结
更深刻的理解计算机实现原理与JVM实现原理
标签:map,int,虚拟机,简单,char,accumulator,printf,operand From: https://www.cnblogs.com/ylc0x01/p/17473709.html