跟我一起写个虚拟机 .Net 7(四)- LC_3 解析实例

没想到这篇文章持续了这么久,越学越深,愣是又买了一本书《计算机系统概论》,当然,也看完了,受益匪浅。

系统化的学习才是正确的学习方式,我大学就没看到过这本书,如果早点看到,可能会更好一些,当然,现在也不晚。

我主要是学习其中原理本质,不至于迷失在书中,要偏向于实践。

资源环境

LC-3 是一个16位地址空间的虚拟机。
1. 地址空间 0-UInt16.MaxValue (0xFFF)。
2. 通用寄存器8个,R0-R7
3. 三个标志寄存器,N ,P,  Z 
4. PC 寄存器
5. 指令集长度16位,操作码固定前四位
6. 支持函数调用

指令集

一共16条指令,可用14条。

1. BR  条件分支
2. ADD  加法
3. LD   Load 加载
4. ST  store 存储
5. JSR 跳转到寄存器
6. AND 与运算
7. LDR 加载寄存器
8. STR 存储寄存器
9. RTI 备用
10. NOT 取反
11. LDI 间接寻址 加载
12. STI 存储,间接寻址
13. Jump (RET) 跳转 返回
14. reserved  备用
15. LEA 加载偏移寄存器
16. TRAP 中断,系统函数调用

解析操作

首先,通过生成命令,汇编asm to bin or obj 文件,其实他们的内容是一致的。

第一行指令,是汇编指令.ORIG的起始地址,所以,要先把它提取出来。

其他命令,都按照下表的格式进行解析

格式也很简单,前四个位,是命令头,直接匹配,后面按照条件位或者寄存器位分割即可。

我这边主要是对 bin文件直接读取,省的 obj 文件,进制互转了。为了容易理解。

需要处理的命令

在上篇文章所介绍的LC3Edit 软件里,输入下边的汇编代码,这是第一步。

.ORIG x3000                        
LEA R0, HELLO_STR                  
PUTs                               
HALT                               
HELLO_STR .STRINGZ "Hello World!"  
WIDTH .FILL x4000
.END

汇编的主要含义是,加载字符串 hello world! ,打印出来,然后,停止运行。

然后按照,2点击 to ASM 汇编,然后,第三步3就会提示0错误,0异常,然后,第四步就会输出相应的编译文件。

我只选择 hello.bin文件进行解析,当然,你解析其他相关文件也是一样的逻辑。只是会复杂一些。

bin文件,格式如下:

整体逻辑

环境

定义了 内存大小和寄存器,以及系统函数调用

public string[] Memory = new string[UInt16.MaxValue];
public Dictionary<Registers, UInt16> Reg = new Dictionary<Registers, UInt16>();
public Dictionary<TrapSet, Action> Traps = new Dictionary<TrapSet, Action>();/// <summary>
/// 寄存器定义
/// </summary>
public enum Registers
{R0 = 0, R1, R2, R3, R4, R5, R6, R7, R8, PC
}public UInt16 PC
{get{return Reg[Registers.PC];}set{Reg[Registers.PC] = value;}
}/// <summary>
/// 标志寄存器
/// </summary>
public enum FlagRegister
{/// <summary>/// 正数/// </summary>POS = 1,/// <summary>/// 0/// </summary>ZRO,/// <summary>/// 负数/// </summary>NEG
}public enum TrapSet
{/// <summary>/// 从键盘输入/// </summary>TRAP_GETC = 0x20,/// <summary>/// 输出字符/// </summary>TRAP_OUT = 0x21,/// <summary>/// 输出字符串/// </summary>TRAP_PUTS = 0x22,/// <summary>/// 打印输入提示,读取单个字符/// </summary>TARP_IN = 0x23,/// <summary>/// 输出字符串/// </summary>TRAP_PUTSP = 0x24,/// <summary>/// 退出程序/// </summary>TRAP_HALT = 0x25,
}
/// <summary>
/// 标志寄存器
/// </summary>
public FlagRegister COND { get; set; }//系统调用函数注册
Traps.Add(TrapSet.TRAP_GETC, Trap_GETC);
Traps.Add(TrapSet.TRAP_OUT, TRAP_OUT);
Traps.Add(TrapSet.TRAP_PUTS, TRAP_PUTS);
Traps.Add(TrapSet.TARP_IN, TRAP_IN);
Traps.Add(TrapSet.TRAP_PUTSP, TRAP_PUTSP);
Traps.Add(TrapSet.TRAP_HALT, TRAP_HALT);/// <summary>
/// 指令集
/// </summary>
public enum InstructionSet
{/// <summary>/// 条件分支/// </summary>BR = 0,/// <summary>///  加法/// </summary>ADD = 1,/// <summary>/// load/// </summary>LD = 2,/// <summary>/// store/// </summary>ST = 3,/// <summary>/// 跳转到寄存器/// </summary>JSR = 4,/// <summary>/// 与运算/// </summary>AND = 5,/// <summary>/// 加载寄存器/// </summary>LDR = 6,/// <summary>/// 存储寄存器/// </summary>STR = 7,/// <summary>/// 备用/// </summary>RTI = 8,/// <summary>/// 取反/// </summary>NOT = 9,/// <summary>/// 间接寻址 加载/// </summary>LDI = 10,/// <summary>/// 存储 间接寻址/// </summary>STI = 11,/// <summary>/// 直接跳/// </summary>JMP = 12,/// <summary>/// reserved/// </summary>RES = 13,/// <summary>/// 加载偏移地址/// </summary>LEA = 14,/// <summary>/// 陷阱,中断,系统函数调用/// </summary>TRAP = 15
}

入口

入口比较简单,直接加载,虚拟机执行。

VM VM = new VM();
VM.LoadBin(File.ReadAllLines(@"hello\hello.bin"));
VM.Run();
Console.WriteLine("LC_3 !");

解析

解析就是把每个命令都解析到具体的命令对象上

public void LoadBin(string[] bincode)
{var ORIG_Base = Convert.ToUInt16(bincode.First(), 2);var ORIG = ORIG_Base;foreach (var item in bincode.Skip(1)){Memory[ORIG] = item;ORIG++;}PC = ORIG_Base;
}

执行

直接执行解析出来的命令集合

从内存中取数据

public void Run()
{IsRuning = true;while (IsRuning){var info = Memory[PC];var cmd = GetCommand(info);switch (cmd.InstructionSet){case InstructionSet.ADD:Add((ADDCommand)cmd);break;case InstructionSet.AND:And((ANDCommand)cmd);break;case InstructionSet.NOT:Not((NOTCommand)cmd);break;case InstructionSet.BR:BR((BRCommand)cmd);break;case InstructionSet.JMP:Jump((JMPCommand)cmd);break;case InstructionSet.JSR:Jump_Subroutine((JSRCommand)cmd);break;case InstructionSet.LD:Load((LDCommand)cmd);break;case InstructionSet.LDI:Load_Indirect((LDICommand)cmd);break;case InstructionSet.LDR:Load_Register((LDRCommand)cmd);break;case InstructionSet.LEA:Load_Effective_address((LEACommand)cmd);break;case InstructionSet.ST:Store((STCommand)cmd);break;case InstructionSet.STI:Store_indirect((STICommand)cmd);break;case InstructionSet.STR:Store_register((STRCommand)cmd);break;case InstructionSet.TRAP:Trap((TRAPCommand)cmd);break;#region 未用case InstructionSet.RTI:break;case InstructionSet.RES:break;#endregion}PC++;}Console.WriteLine("虚拟机停止了运行!");
}

指令解析,统一化

public class ANDCommand : ACommand
{public ANDCommand() : base(InstructionSet.AND){bitInfo.AddInfo(nameof(this.InstructionSet), 15, 12);bitInfo.AddInfo(nameof(this.DR), 11, 9);bitInfo.AddInfo(nameof(this.SR1), 8, 6);bitInfo.AddInfo(nameof(this.IsImmediateNumber), 5, 5, nameof(this.ImmediateNumber), nameof(this.SR2));bitInfo.AddInfo(nameof(this.ImmediateNumber), 4, 0);bitInfo.AddInfo(nameof(this.SR2), 2, 0);}/// <summary>/// 目的寄存器/// </summary>public Registers DR { get; set; }/// <summary>/// 源寄存器1/// </summary>public Registers SR1 { get; set; }public bool IsImmediateNumber { get; set; }/// <summary>/// 源寄存器2/// </summary>public Registers SR2 { get; set; }public UInt16 ImmediateNumber { get; set; }
}

初始化的时候,根据指令集的前四位操作码,直接定位到命令对象,对象,就会按照指令本身的各个位的含义去初始化各个寄存器等信息。

在解析的时候,只需要 GetCommand(info); 即可

public static ACommand GetCommand(string item)
{InstructionSet set = (InstructionSet)Convert.ToInt32(new string(item.Take(4).ToArray()), 2);ACommand aCommand = null;switch (set){case InstructionSet.BR:{aCommand = new BRCommand();aCommand.BinToCommand(item);}break;case InstructionSet.ADD:{aCommand = new ADDCommand();aCommand.BinToCommand(item);}break;case InstructionSet.LD:{aCommand = new LDCommand();aCommand.BinToCommand(item);}break;case InstructionSet.ST:{aCommand = new STCommand();aCommand.BinToCommand(item);}break;case InstructionSet.JSR:{aCommand = new JSRCommand();aCommand.BinToCommand(item);}break;case InstructionSet.AND:{aCommand = new ANDCommand();aCommand.BinToCommand(item);}break;case InstructionSet.LDR:{aCommand = new LDRCommand();aCommand.BinToCommand(item);}break;case InstructionSet.STR:{aCommand = new STRCommand();aCommand.BinToCommand(item);}break;case InstructionSet.RTI:{aCommand = new RTICommand();aCommand.BinToCommand(item);}break;case InstructionSet.NOT:{aCommand = new NOTCommand();aCommand.BinToCommand(item);}break;case InstructionSet.LDI:{aCommand = new LDICommand();aCommand.BinToCommand(item);}break;case InstructionSet.STI:{aCommand = new STICommand();aCommand.BinToCommand(item);}break;case InstructionSet.JMP:{aCommand = new JMPCommand();aCommand.BinToCommand(item);}break;case InstructionSet.RES:{aCommand = new RESCommand();aCommand.BinToCommand(item);}break;case InstructionSet.LEA:{aCommand = new LEACommand();aCommand.BinToCommand(item);}break;case InstructionSet.TRAP:{aCommand = new TRAPCommand();aCommand.BinToCommand(item);}break;}return aCommand;
}

指令函数示例

其实就是对特定逻辑操作的封装,指令基础操作,跟之前的几乎一样。

ADD

public void Add(ADDCommand command)
{if (command.IsImmediateNumber){Reg[command.DR] = (ushort)(Reg[command.SR1] + command.ImmediateNumber);}else{Reg[command.DR] = (ushort)(Reg[command.SR1] + Reg[command.SR2]);}Console.WriteLine($"{command.DR} : {Reg[command.DR]}");Update_flags(Registers.R0);
}

AND

public void And(ANDCommand command)
{if (command.IsImmediateNumber){Reg[command.DR] = (ushort)(Reg[command.SR1] + command.ImmediateNumber);}else{Reg[command.DR] = (ushort)(Reg[command.SR1] + Reg[command.SR2]);}Console.WriteLine($"{command.DR} : {Reg[command.DR]}");Update_flags(Registers.R0);
}

NOT

public void Not(NOTCommand command)
{Reg[command.DR] = (ushort)~Reg[command.SR];Update_flags(Registers.R0);
}

JUMP

public void Jump(JMPCommand command)
{PC = Reg[command.BaseR];
}

Trap

public void Trap(TRAPCommand command)
{Traps.TryGetValue((TrapSet)command.Trapverct, out var action);action?.Invoke();
}

运行结果

可喜可贺,终于搞出来了。

总结

只能说获益匪浅,很多东西,就算是很简单,你也得动手敲一遍,它就成了技能,而不仅仅留存在书和脑海的记忆中。

做完一件事情,再继续做下一件事情,这就是计算机的原理。

当然,LC-3细节还有很多没有补足,我想,对我来讲最重要的事它物理组件之间的逻辑关系,而不是其他。

代码地址

https://github.com/kesshei/VirtualMachineDemo.git

https://gitee.com/kesshei/VirtualMachineDemo.git

一键三连呦!,感谢大佬的支持,您的支持就是我的动力!

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

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

相关文章

STM32F4之系统滴答定时器

一、系统滴答定时器概述 传统定时器&#xff1a;如手机闹钟&#xff0c;闹钟等就是一个简单地计数器。 定时器概念&#xff1a;由时钟源计数器计数值组成的计数单元。 系统嘀嗒定时器首先是存在于内核里&#xff0c;系统嘀嗒时钟假如用的是同一个内核那么里面相关的配置&…

Android问题笔记 - NoSuchmethodException: could not find Fragment constructor

点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册点击跳转>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&…

守护进程深度分析

思考 代码中创建的会话&#xff0c;如何关联控制终端&#xff1f; 新会话关联控制终端的方法 会话首进程成功打开终端设备 (设备打开前处于空闲状态) 1、关闭标准输入输出和标准错误输出2、将 stdin 关联到终端设备&#xff1a;STDIN_FILENO > 03、将 stdout 关联到终端设…

每日刷题|贪心算法初识

食用指南&#xff1a;本文为作者刷题中认为有必要记录的题目 推荐专栏&#xff1a;每日刷题 ♈️今日夜电波&#xff1a;悬溺—葛东琪 0:34 ━━━━━━️&#x1f49f;──────── 3:17 &#x1f…

idea中还原dont ask again

背景 在使用idea打开另外一个项目的时候&#xff0c;一不小心勾选为当前项目而且是不在下次询问&#xff0c;导致后面每次打开新的项目都会把当前项目关闭&#xff0c;如下图所示 下面我们就一起看一下如何把这个询问按钮还原回来 preferences/settings->Appearance&…

展馆导览系统之AR互动式导航与展品语音讲解应用

一、项目背景 随着科技的进步和人们对于文化、艺术、历史等方面需求的提升&#xff0c;展馆在人们的生活中扮演着越来越重要的角色。然而&#xff0c;传统的展馆导览方式&#xff0c;如纸质导览、人工讲解等&#xff0c;已无法满足参观者的多元化需求。为了提升参观者的体验&a…

vulnhub靶机Funbox11

下载地址&#xff1a;Funbox: Scriptkiddie ~ VulnHub 主机发现 arp-scan -l 目标192.168.21.164 端口扫描 nmap --min-rate 1000 -p- 192.168.21.164 端口好多处理一下吧 nmap --min-rate 1000 -p- 192.168.21.164 |grep open |awk -F / {print $1} |tr \n , 端口服务版本…

【ARM裸机】ARM入门

1.ARM成长史 2.ARM的商业模式和生态系统 ARM只设计CPU&#xff0c;但是不生产CPU 3.为什么使用三星&#xff1a;S5PV210 4.各种版本号 0. ARM和Cortex Cortex就是ARM公司一个系列处理器的名称。比如英特尔旗下处理器有酷睿&#xff0c;奔腾&#xff0c;赛扬。ARM在最初的处理器…

P1950 长方形

题目&#xff1a; P1950 长方形 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 算法&#xff1a; dp动态规划 代码&#xff1a; #include<iostream> #include<string> typedef unsigned long long ull; const int N 1010; using namespace std;int r, c, i, …

打印新闻标题,使用封装get、set方法,打印前15个字符串

package day21; import java.util.ArrayList; import java.util.Collections;/*** author monian* Wo yi wu ta,wei shou shu er!*/ public class Homework01 {SuppressWarnings({"all"})public static void main(String[] args) {News news1 new News("新冠确…

6.5 Elasticsearch(五)Spring Data Elasticsearch - 增删改查API

文章目录 1.Spring Data Elasticsearch2.案例准备2.1 在 Elasticsearch 中创建 students 索引2.2 案例测试说明 3.创建项目3.1 新建工程3.2 新建 springboot module&#xff0c;添加 spring data elasticsearch 依赖3.3 pom.xml 文件3.4 application.yml 配置 4.Student 实体类…

在 Python 中使用 Pillow 进行图像处理【2/4】

第二部分 一、说明 该文是《在 Python 中使用 Pillow 进行图像处理》的第二部分&#xff0c;主要介绍pil库进行一般性处理&#xff1a;如&#xff1a;图像卷积、钝化、锐化、阈值分割。 二、在 Python 中使用 Pillow 进行图像处理 您已经学习了如何裁剪和旋转图像、调整图像大…

机器学习终极指南:统计和统计建模03/3 — 第 -3 部分

系列上文&#xff1a;机器学习终极指南&#xff1a;特征工程&#xff08;02/2&#xff09; — 第 -2 部分 一、说明 在终极机器学习指南的第三部分中&#xff0c;我们将了解统计建模的基础知识以及如何在 Python 中实现它们&#xff0c;Python 是一种广泛用于数据分析和科学计…

Spring Security认证架构介绍

在之前的Spring Security&#xff1a;总体架构中&#xff0c;我们讲到Spring Security整个架构是通过Bean容器和Servlet容器对过滤器的支持来实现的。我们将从过滤器出发介绍Spring Security的Servlet类型的认证架构。 1.AbstractAuthenticationProcessingFilter AbstractAut…

Git的介绍和命令汇总

目录 一、git介绍 1、git的工作区域 2、git中文件的四种状态 二、常用命令 1、基础命令 2、提交类命令 3、删除类命令 4、分支类相关命令 5、 查看类相关命令 6、撤销类命令 一、git介绍 1、git的工作区域 在Git中&#xff0c;有四个工作区域&#xff1a;工作区域&am…

CorelDRAW Graphics Suite2024完整版最新功能介绍

CorelDRAW平面设计软件通常也被叫做CDR&#xff0c;CDR广泛应用于排版印刷、矢量图形编辑及网页设计等领域。通过CorelDRAW体验极具个性的自由创作&#xff0c;大胆展现真我&#xff0c;交付出众的创意作品。CorelDRAW拥有矢量插图、页面布局、图片编辑和设计工具&#xff0c;无…

迅为RK3568开发板RTMP推流之视频监控

1 搭建 RTMP 媒流体服务器 nginx-rtmp 是一个基于 nginx 的 RTMP 服务模块&#xff0c;是一个功能强大的流媒体服务器模块&#xff0c; 它提供了丰富的功能和灵活的配置选项&#xff0c;适用于构建各种规模的流媒体平台和应用。无论是搭建实时视频直播平台、点播系统或多屏互…

leetcode 114. 二叉树展开为链表

2023.10.22 本题第一反应就是将 原二叉树的节点值 用先序遍历的方式保存到一个集合数组中。然后再重新构造出新的二叉树。 java代码如下&#xff1a; /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode rig…

我国跨境电商行业研究报告(2022)

我国跨境电商行业研究报告 我国跨境电商规模突飞猛进&#xff0c;2022年进出口规模超2万亿元&#xff0c;2023年上半年跨境电商出口8210亿元&#xff0c;增长19.9%。全国跨境电商主体已超10万家&#xff0c;近年来涌现出一批上市公司&#xff0c;以及广州希音等全球独角兽企业。…

curl命令服务器上执行http请求

1. 现在本地使用postman生成curl命令 注意: 将ip改成127.0.0.1,端口是实际服务运行的端口 curl --location --request POST http://127.0.0.1:63040/content/course/list?pageNo1&pageSize2 \ --header Content-Type: application/json \ --data-raw {"courseName&q…