C11编写简易16位虚拟机

虚拟机

在计算领域,VM(虚拟机)是一个术语,指的是模拟/虚拟化计算机系统/架构的系统。

一般来说,虚拟机有两类:

  • 系统虚拟机提供真实机器的完整替代品。 它们实现了足够的功能,允许操作系统在它们上运行。 它们可以共享和管理硬件,有时多个环境可以在同一台物理机器上运行而不会互相妨碍。
  • 处理虚拟机更简单,旨在在与平台无关的环境中执行计算机程序。 JVM 是进程虚拟机的一个很好的例子。

在本文中,我们将开发一个简单的进程虚拟机,旨在在独立于平台的环境中执行简单的计算机程序。 我们的虚拟机基于 LC-3 计算机架构,并且能够解释和执行 LC3 汇编代码(的子集)。

Little Computer 3,或 LC-3,是一种计算机教育编程语言,一种汇编语言,一种低级编程语言。 它具有相对简单的指令集,但可用于编写中等复杂的汇编程序,并且是 C 编译器的可行目标。 该语言比 x86 汇编语言简单,但具有许多与更复杂语言类似的功能。 这些功能使其值得入门教学,因此它最常用于向计算机科学和计算机工程学生教授编程和计算机体系结构基础知识

为简单起见,我们特意从以下功能中剥离了 LC-3 实现:中断处理、优先级、进程、状态寄存器 (PSR)、特权模式、管理程序堆栈、用户堆栈。 我们将只虚拟化最基本的硬件,并且我们将通过陷阱与外界(stdin、stdout)进行交互。

我们受 LC-3 启发的 VM 与当今大多数通用计算机一样,基于冯·诺依曼计算机模型,它将具有三个主要组件:CPU、主存储器、输入/输出设备。

实现

我们的虚拟机功能如下:

  • 我们将程序加载到主存中
  • 在RPC寄存器中,我们保存当前需要执行的指令
  • 我们从指令中获取操作码(前 4 位),并据此解码其余参数
  • 我们执行与给定指令相关的方法
  • 我们增加 RPC 并继续下一条指令

内存

我们的机器有 W=UINT16_MAX 个字,每个字 N=16 位。从 C 的角度来看,我们的内存可以定义为:

uint16_t PC_START = 0x3000;
uint16_t mem[UINT16_MAX+1] = {0};

寄存器

我们的 VM 共有 10 个寄存器,每个寄存器 16 位:

从代码的角度来看,我们可以按如下方式实现它们:

enum regist { R0 = 0, R1, R2, R3, R4, R5, R6, R7, RPC, RCND, RCNT };
uint16_t reg[RCNT] = {0};

指令

指令就像我们向虚拟机发出的命令。

为了提取操作码本身,我们可以编写一个实用宏来应用简单的按位技巧:

#define OPC(i) ((i)>>12)

我们可以在 C 中执行的一个好技巧(从数据建模的角度来看)是将所有可能的指令(及其关联的 C 函数)保存在数组中。 索引将代表实际的操作码(毕竟,操作码是从 0 到 15 的数字),并且该值将是指向相应 C 函数的指针。

#define NOPS (16) // number of instructions
typedef void (*op_ex_f)(uint16_t instruction);
//
// ... other operations here
//
static inline void add(uint16_t i)  { /* code here */ }
static inline void and(uint16_t i)  { /* code here */ }
//
// ... other operations here
//
op_ex_f op_ex[NOPS] = { br, add, ld, st, jsr, and, ldr, str, rti, not, ldi, sti, jmp, res, lea, trap 
};

加法

逻辑位“与”

ld - 加载 RPC + 偏移量

ldi - 间接加载

ldr - 加载+偏移量

lea - 加载有效地址

not - 按位求补

st - 存储

sti - 间接存储

str - 存储+偏移量

jump - 跳转

加载和执行程序

我们只缺少两个功能:主循环和加载程序的能力。

我们虚拟机的主循环如下所示:

bool running=true;
uint16_t PC_START = 0x3000;
void start(uint16_t offset) { reg[RPC] = PC_START + offset; // The RPC is setwhile(running) {uint16_t i = mr(reg[RPC]++); // We extract instructions from the memory// location pointed by RPC           // We (auto)increment RPC       op_ex[OPC(i)](i);            // We execute each instruction}
}

现在,唯一缺少的是将程序加载到我们的虚拟机中的能力,在这方面我们将编写一个 ld_img 方法,能够将二进制文件直接加载到我们的主内存中:

void ld_img(char *fname, uint16_t offset) {// Open (binary) file containing the VM programFILE *in = fopen(fname, "rb");  if (NULL==in) {fprintf(stderr, "Cannot open file %s.\n", fname);exit(1);    }// The position from were we start copying the file// to the main memoryuint16_t *p = mem + PC_START + offset;// Load the program in memoryfread(p, sizeof(uint16_t), (UINT16_MAX-PC_START), in);// Close the file streamfclose(in);
}

该方法返回 void 并接受两个输入参数:

  • 包含我们程序的二进制文件的路径
  • 我们开始将第一条程序指令加载到主内存中的偏移量

我们虚拟机的主要方法如下所示:

int main(int argc, char **argv) {ld_img(argv[1], 0x0);start(0x0);return 0;
}

我们的第一个程序,将从键盘读取两个数字并将它们的总和打印到标准输出。

0xF026    //  1111 0000 0010 0110  TRAP tinu16      ;read an uint16_t in R0
0x1220    //  0001 0010 0010 0000  ADD R1,R0,x0     ;add contents of R0 to R1
0xF026    //  1111 0000 0010 0110  TRAP tinu16      ;read an uint16_t in R0
0x1240    //  0001 0010 0010 0000  ADD R1,R1,R0     ;add contents of R0 to R1
0x1060    //  0001 0000 0110 0000  ADD R0,R1,x0     ;add contents of R1 to R0
0xF027    //  1111 0000 0010 0111  TRAP toutu16     ;show the contents of R0 to stdout
0xF025    //  1111 0000 0010 0101  HALT             ;halt

语法对用户不友好,不是吗?我们的程序其实就是这一系列数字:0xF026 0x1220 0xF026 0x1240 0x1060 0xF027 0xF025。但如果我们仔细观察,就会发现在这些数字中我们一直在编码汇编指令。

例如,让我们看一下这个数字0xF026。其二进制表示为1111 0000 0010 0110。很容易看出1111是trap的编码,TRAPVECT是100111,对应tinu16。

或者为了更直观的表示,我们来分析 0x1220:

0x1220 ->0001   001 000 1  00000 
ADD    R1  R0     IMM5=0     

运行我们的第一个程序

#include <stdio.h>
#include <stdlib.h>uint16_t program[] = {/*mem[0x3000]=*/    0xF026,    //  1111 0000 0010 0110             TRAP trp_in_u16  ;read an uint16_t from stdin and put it in R0/*mem[0x3002]=*/    0x1220,    //  0001 0010 0010 0000             ADD R1,R0,x0     ;add contents of R0 to R1/*mem[0x3003]=*/    0xF026,    //  1111 0000 0010 0110             TRAP trp_in_u16  ;read an uint16_t from stdin and put it in R0/*mem[0x3004]=*/    0x1240,    //  0001 0010 0010 0000             ADD R1,R1,R0     ;add contents of R0 to R1/*mem[0x3006]=*/    0x1060,    //  0001 0000 0110 0000             ADD R0,R1,x0     ;add contents of R1 to R0/*mem[0x3007]=*/    0xF027,    //  1111 0000 0010 0111             TRAP trp_out_u16;show the contents of R0 to stdout/*mem[0x3006]=*/    0xF025,    //  1111 0000 0010 0101             HALT             ;halt
};int main(int argc, char** argv) {char *outf = "sum.obj";FILE *f = fopen(outf, "wb");if (NULL==f) {fprintf(stderr, "Cannot write to file %s\n", outf);}size_t writ = fwrite(program, sizeof(uint16_t), sizeof(program), f);fprintf(stdout, "Written size_t=%lu to file %s\n", writ, outf);fclose(f);return 0;
}

源代码

参阅一 - 亚图跨际
参阅二 - 亚图跨际

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/235599.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MIT 6.824 练习1

Hi, there! 这是一份根据 MIT 6.824(2021) 课程的第 2 课的课堂示例代码改编的 2 个 go 语言编程练习。像其他的编程作业一样&#xff0c;我去除了核心部分&#xff0c;保留了代码框架&#xff0c;并编写了每一步的提示 练习代码在本文的最后面 爬虫 在第一部分&#xff0c;…

浅谈能耗系统在马来西亚连锁餐饮业的应用

1.背景信息 Background 针对连锁餐饮业能耗高且能源管理不合理的问题&#xff0c;利用计算机网络技术、通讯技术、计量控制技术等信息化技术&#xff0c;实现能源资源分类分项计量和能源资源运行监管功能&#xff0c;清晰描述各分店总的用能现状&#xff1b;实时监测各供电回路…

Hal深入实战/perfetto-systrace实战/SurfaceFlinger合集-安卓framework开发实战开发

背景 hi&#xff0c;粉丝朋友们&#xff1a; 大家好&#xff01; 下面来介绍一下新的framework专题halperfettosurafceflinger&#xff0c;这个专题主要就是分为3大块&#xff0c;但是彼此直接又是相互关联的。 比如surfaceflingre模块深入分析需要用到hal相关的模块&#xff…

IDEA创建springboot工程

选择spring boot的版本和依赖 finish创建完成 删除无用的文件

【make/Makefile】Linux下进度条的设计与实现

一、简单介绍make/Makefile Makefile 和 makefile 不区分大小写&#xff0c;但是一定只能是 “makefile” &#xff01;&#xff01;&#xff01; make 是一个指令&#xff0c;makefile 是一个文件。 Makefile 格式形式&#xff1a; 使用 make 生成目标文件时&#xff0c;默认…

论文解读:On the Integration of Self-Attention and Convolution

自注意力机制与卷积结合&#xff1a;On the Integration of Self-Attention and Convolution(CVPR2022) 引言 1&#xff1a;卷积可以接受比较大的图片的&#xff0c;但自注意力机制如果图片特别大的话&#xff0c;运算规模会特别大&#xff0c;即上图中右边(卷积)会算得比较快…

Web前端-JavaScript(js数组和函数)

文章目录 1.数组1.1 数组的概念1.2 创建数组1.3 获取数组中的元素1.4 数组中新增元素1.5 遍历数组 2.函数2.1 函数的概念2.2 函数的使用函数声明调用函数函数的封装 2.3 函数的参数函数参数语法函数形参和实参数量不匹配时 2.4 函数的返回值2.4.1 案例练习 2.5 arguments的使用…

Python开发GUI常用库PyQt6和PySide6介绍之二:设计师(Designer)注意事项

Python开发GUI常用库PyQt6和PySide6介绍之二&#xff1a;设计师&#xff08;Designer&#xff09;注意事项 PySide6和PyQt6都有自己的设计师&#xff08;Designer&#xff09;&#xff0c;用于可视化地设计和布局GUI应用程序的界面。这些设计师提供了丰富的工具和功能&#xf…

快速入门 — — 在Moonbeam上开发

访问熟悉的以太坊工具是一回事&#xff0c;获得顶级支持、拥有构建突破性跨链应用程序的资源是另一回事。 Moonbeam汇集了通过集成互操作性解决方案访问任何链的能力、具有完全以太坊兼容性的理想开发环境&#xff0c;以及使用Substrate在波卡上安全扩展的能力。 开始在Moonb…

ARMv8-A 架构和处理器

ARM 架构可以追溯到 1985 年&#xff0c;但它并没有保持静止。相反&#xff0c;自早期的 ARM 内核以来&#xff0c;它已经得到了大规模的发展&#xff0c;每一步都增加了特性和功能&#xff1a; ARMv4 及更早版本 这些早期的处理器仅使用 ARM 32 位指令集。 ARMv4T ARMv4T架…

MT6785|MTK6785安卓核心板功能规格介绍_Helio G95核心板

MT6785安卓核心板是一款功能强大的工业级4G智能模块&#xff0c;它采用了Android 9.0操作系统。该核心板内置了蓝牙、FM、WLAN和GPS模块&#xff0c;具有高度集成的基带平台&#xff0c;结合了调制解调器和应用处理子系统&#xff0c;以支持LTE/LTE-A和C2K智能终端应用。 MTK67…

2023年12月GESP Python三、四级编程题真题解析

三、2023年12月GESP Python三级编程题 【三级编程题1】 【试题名称】&#xff1a;小猫分鱼 【问题描述】 海滩上有一堆鱼&#xff0c;N只小猫来分。第一只小猫把这堆鱼平均分为N份&#xff0c;多了i<N条鱼&#xff0c;这只小猫把多的i条鱼扔入海中&#xff0c;拿走了一份…

【小沐学Unity3d】3ds Max 减面工具:Simplyon(Unity3d,Python)

文章目录 1、简介2、下载安装2.1 安装Simlygon插件2.2 安装USD插件 3、使用测试4、Python测试结语 1、简介 Simplygon 带有一个 Unity 插件&#xff0c;它公开了优化功能&#xff0c;例如缩减、聚合、重新划分网格、冒名顶替者&#xff08;SingleView、BillboardCloud / Veget…

electron兼容统信UOS系统过程中的坑

这里写目录标题 找统信支持人员咨询过&#xff0c;他们说不对electron提供支持&#xff0c;如果需要兼容统信UOS还是建议换个开发技术gbm_bo_map--no-sandboxNo protocol specified任务栏图标总结 找统信支持人员咨询过&#xff0c;他们说不对electron提供支持&#xff0c;如果…

Module build failed: TypeError: this.getOptions is not a function

在使用webpack打包出现以上错误时&#xff0c;可能是你安装的css-loader和style-loader的版本过高。 我用的webpack版本是3.6.0 因此需要降低一下版本 在你编辑器终端输入以下命令&#xff1a; npm install css-loader3.6.0 npm install --save-dev style-loader1.00 然后接下…

hyper-v ubuntu2204指定静态ip地址

虚拟机静态IP设置 虚拟机每次重新启动&#xff0c;都会动态分配IP&#xff0c;这导致我们无法使用一个固定的ip连接到虚拟机内部。解决该问题的最直接有效的办法就是给虚拟机绑定2张网卡&#xff0c;一张用于连接外网、一张用于连接内网。 init 0 关机&#xff0c;也可以从管…

如何用 CleanMyMac 来保护 Mac 隐私

大家早上好&#xff0c;中午好&#xff0c;下午好&#xff0c;晚上好。 在我们使用MacBook上的自带浏览器-Safari&#xff08;或者一些其他浏览器&#xff09;进行网页浏览的时候&#xff0c;往往会留下一些痕迹。如果这些痕迹涉及一些敏感数据信息的话&#xff0c;那么我们肯…

idea 如何使用 JaCoCo 跑覆盖率

背景介绍 什么代码覆盖&#xff1f; 代码覆盖(Code coverage)是软件测试中的一种度量&#xff0c;描述程序中源代码被测试的比例和程度&#xff0c;所得比例称为代码覆盖率。简单来理解&#xff0c;就是单元测试中代码执行量与代码总量之间的比率。 Java常用的单元测试覆盖率…

K8S学习指南(31)-k8s网络插件calico

文章目录 引言Calico的特点1. 纯Layer 3架构2. BGP路由3. ACL&#xff08;Access Control List&#xff09;4. 网络流量加密 Calico的优势1. 高性能2. 易于管理3. 强大的安全性4. 可扩展性 Calico的劣势1. 对Underlay网络的依赖2. 相对复杂的部署 在Kubernetes中部署Calico的示…

第四十一章 XML 映射参数摘要

文章目录 第四十一章 XML 映射参数摘要 第四十一章 XML 映射参数摘要 TopicParameters启用 XML 映射。XMLENABLED 类参数将属性映射到元素或属性。XMLPROJECTION property parameter ("NONE", "ATTRIBUTE", "XMLATTRIBUTE", "CONTENT"…