首页 > 其他分享 >简单虚拟机

简单虚拟机

时间:2023-06-11 22:00:14浏览次数:37  
标签:map int 虚拟机 简单 char accumulator printf operand

目录

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中,然后指令字被一分为二,左边两位数字和右边两位数字被分别存人变量operationCodeoperand中。

操作码

image

0x03 应用

需求

输入两个值,比较两个值的大小,输出大值

实现

操作码文件sml.txt:

+1009 	// 输入一个字并存储到09内存
+1010	// 输入一个字并存储到10内存
+2009	// 将09内存的值加载到累加器中
+3110	// 将累加器中的值减去10内存中的值,结果保存在累加器中
+4107	// 如果累加器中的值为负转移到07内存执行
+1109	// 将09内存的值输出到终端
+4300	// 退出程序
+1110 	// 将10内存的值输出到终端
+4300 	// 退出程序
+0000	// 无操作
+0000 	// 无操作
-9999 	// 程序结束

执行结果:

image

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

相关文章

  • 代理模式的简单理解
    1.废话​ 上次io流联系的时候,有一个地方有待优化,计划使用代理模式。恰逢开始写博客,着闲着也是闲着记录一下丰富一下我的博客记录,之前看过一些网上其他的代理的讲解都很高端,我这技术有限只说一下自己理解的代理模式2.背景​ 之前统计项目中废没在被使用的VUE文件时写了一个遍历......
  • VMware Workstation 桥接模式的网卡上让虚拟机使用 VLAN 的正确方法
    要解决的问题有一个虚拟机运行在VMWareworkstation中,如何让这个虚拟机桥接到宿主机的网络上的某个VLAN?常见使用场景只有一个网口的单臂软路由,同时宿主机为Windows,虚拟机软件为VMwareWorkstation,在虚拟机中运行OpenWrt需要在一个网口上,利用VLAN实现虚拟WAN口上的P......
  • 简单实现JPA实体继承映射
    一个简单实现父类加注解:@MappedSuperclass@MappedSuperclasspublicclassTraceabilityimplementsSerializable{/***创建时间**/privateDatecreateDt;/***更新时间**/privateDateupdateDt;/***创建人员......
  • 非常简单的Python HTTP服务
       如果你急需一个简单的WebServer,但你又不想去下载并安装那些复杂的HTTP服务程序,比如:Apache,ISS等。那么,Python可能帮助你。使用Python可以完成一个简单的内建HTTP服务器。于是,你可以把你的目录和文件都以HTTP的方式展示出来。佻只需要干一件事情,那就是安装一个Python。......
  • Python使用Redis实现一个简单作业调度系统
        概述Redis作为内存数据库的一个典型代表,已经在很多应用场景中被使用,这里仅就Redis的pub/sub功能来说说怎样通过此功能来实现一个简单的作业调度系统。这里只是想展现一个简单的想法,所以还是有很多需要考虑的东西没有包括在这个例子中,比如错误处理,持久化等。下面是实现上......
  • Python使用multiprocessing实现一个最简单的分布式作业调度系统
    介绍Python的multiprocessing模块不但支持多进程,其中managers子模块还支持把多进程分布到多台机器上。一个服务进程可以作为调度者,将任务分布到其他多个机器的多个进程中,依靠网络通信。想到这,就在想是不是可以使用此模块来实现一个简单的作业调度系统。实现Job首先创建一个Job类,为......
  • 利用Kali进行简单的 SYN FLOOD 攻击测试
    本文实验旨在简单介绍下使用kali的自带工具hping3进行SYNFLOOD(属于典型的DOS/DDOS攻击)测试攻击,实验环境均在VMware虚拟机内,并无涉及真实IP地址。若因传播或利用本文所提供的信息而造成任何直接或间接的后果,均由使用者本人负责,作者不为此承担任何责任!打开Kali虚拟机后,进入管理......
  • Debian 12 x86_64 OVF (sysin) - VMware 虚拟机模板
    Debian12x86_64OVF(sysin)-VMware虚拟机模板请访问原文链接:https://sysin.org/blog/debian-12-ovf/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.orgDebianGNU/Linux12(bookworm)(Linuxdebian6.1.0-amd64)部署截图及说明自定义OVF属性填写说明:......
  • .net framework生成Nuget包简单教程
    前言nuget包生成在.netframework和.netcore/.netstandard下的是不同的。.netframework中稍微复杂些,下面记录了我自己在.netframework中本地生成Nuget包的过程,只涉及主要步骤,更多细节参见微软官方文档。步骤1,到https://www.nuget.org/downloads下载nuget.exe,下载完我放在D......
  • phonegap3.1.0自学笔记01_命令行界面(CLI)简单使用
    要使用phonegap的CLI必须首先安装好phonegap,phonegap的安装还请参看我的另外一篇文章:windows7搭建phonegap3Android开发环境。本篇文章介绍CLI的简单使用,由于本人水平有限,还请大侠不要拍砖。 phonegap3.1.0使用命令行去创建应用程序的框架,然后我们可以基于命令行创建的程序再去进......