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

news2024/11/19 16:33:17

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

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

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

资源环境

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.coloradmin.cn/o/1120479.html

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

相关文章

可视化 | python可视化相关库梳理(自用)| pandas | Matplotlib | Seaborn | Pyecharts | Plotly

文章目录 &#x1f4da;Plotly&#x1f407;堆叠柱状图&#x1f407;环形图&#x1f407;散点图&#x1f407;漏斗图&#x1f407;桑基图&#x1f407;金字塔图&#x1f407;气泡图&#x1f407;面积图⭐️快速作图工具&#xff1a;plotly.express&#x1f407;树形图&#x1f…

MySQL 排名函数 RANK, DENSE_RANK, ROW_NUMBER

文章目录 1 排名函数有哪些?2 SQL 代码实现2.1 RANK2.2 DENSE_RANK2.3 ROW_NUMBER 1 排名函数有哪些? RANK(): 并列跳跃排名, 并列即相同的值, 相同的值保留重复名次, 遇到下一个不同值时, 跳跃到总共的排名DENSE_RANK(): 并列连续排序, 并列即相同的值, 相同的值保留重复名…

图详解第六篇:多源最短路径--Floyd-Warshall算法(完结篇)

文章目录 多源最短路径--Floyd-Warshall算法1. 算法思想2. dist数组和pPath数组的变化3. 代码实现4. 测试观察5. 源码 前面的两篇文章我们学习了两个求解单源最短路径的算法——Dijkstra算法和Bellman-Ford算法 这两个算法都是用来求解图的单源最短路径的算法&#xff0c;区别在…

effective c++学习笔记(后四章)

六 继承与面向对象设计 红色字 \color{FF0000}{红色字} 红色字 32 确定你的public继承塑模出 is-a关系 如果你令class D (“Derived”)以public形式继承class B (“Base”)&#xff0c;你便是告诉C编译器&#xff08;以及你的代码读者&#xff09;说&#xff0c;每一个类型为…

基于目录的ant任务

一些任务利用目录树来执行一些动作 一些任务利用目录树来执行一些动作。例如&#xff0c;javac这个任务就是一个基于目录的任务&#xff0c;它将一个目录中的.java文件编译为.class文件。因为一些这样的任务在目录树上做很多的工作&#xff0c;所以这些任务本身充当了隐含的文…

C# Socket通信从入门到精通(2)——多个同步TCP客户端C#代码实现

前言: 我们在开发Tcp客户端程序的时候,有时候在同一个软件上我们要连接多个服务器,这时候我们开发的一个客户端就不够使用了,这时候就需要我们开发出来的软件要支持连接多个服务器,最好是数量没有限制,这样我们就能应对任意数量的服务器连接,由于我们开发的Tcp客户端程…

7个可能改变AEC行业的AI工具

推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 人工智能&#xff08;AI&#xff09;工具在各个行业中越来越受欢迎&#xff0c;ChatGDP的推出无疑让人们看到了人工智能所能提供的可能性。 然而&#xff0c;人工智能不仅仅是生成文本或图形——它可以用于各种设置。 建筑…

【面试题】JDBC桥接模式如何实现的?

Hello 大家好&#xff0c;我是小米&#xff01;很高兴又和大家见面啦&#xff01;今天的主题是——"面试题&#xff1a;JDBC桥接模式如何实现的&#xff1f;"。 相信大家都听说过JDBC&#xff08;Java Database Connectivity&#xff09;&#xff0c;它是Java中连接…

QT判断平台和生成版本设置输入目录

QT判断平台和生成版本设置输入目录 pro工程文件中常用的宏定义Chapter1 QT判断平台和生成版本设置输入目录Chapter2 Qt pro文件中判断 x86/arm(aarch64)交叉编译环境&#xff0c;区分 linux/windows系统, debug/release版本Chapter3 Qt的版本判断、跨平台选择与pro工程文件输出…

231022|redis_demo

安装 https://github.com/tporadowski/redis https://github.com/redis/redis-py/ 解压后要先配置redis.windows.conf文件&#xff0c;里面有本地端口和密码设置 默认host:127.0.0.1 port:6379 打开命令行到redis文件夹下&#xff0c;redis-server.exe redis.windows.conf输入即…

1024我来利用DOS攻击你的电脑了?(第十三课)

1024我来利用DOS攻击你的电脑了&#xff1f;(第十三课) 本文章设计安全领域的重点问题 学习本文章时 请扎在初学者的角度学习 用于正途 一 国家安全法 1 安全法律法规 《宪法》中的相关规定 案例&#xff1a; 大山破解同事小美私人邮箱密码&#xff0c;读取其往来邮件 邮箱…

Go并发编程之四

一、前言 今天我们介绍一下Go并发编程另外一个重要概念【多路复用】&#xff0c;多路复用最开始是在网络通讯领域&#xff08;硬件&#xff09;应用&#xff0c;指的是用同一条线路承载多路信号进行通信的方式&#xff0c;有频分多路复用、时分多路复用等等技术&#xff0c;然…

组合数(递推版)的初始化

初始考虑为将第一列数和斜对角线上的数进行初始化。 橙色方块由两个绿色方块相加而来&#xff0c;一个为1&#xff0c;一个为0&#xff0c;所以斜对角线都为1&#xff0c;可以通过计算得来&#xff0c;不需要初始化&#xff0c;需要与码蹄集盒子与球 第二类Stirling数&#xf…

【Linux】命令行参数和环境变量

命令行参数 其实main函数是可以传参数的&#xff0c;也叫做命令行参数。我们这里先介绍main函数的前两个参数 argc代表的是指针数组的元素个数&#xff0c;argv是一个指针数组&#xff0c;指针指向字符串。argv不可能为空&#xff0c;argv【0】存储该进程的名字 例如 ls -a -…

【数据结构】830+848真题易错题汇总(10-23)

【数据结构】830848易错题汇总(10-23) 文章目录 【数据结构】830848易错题汇总(10-23)选择题填空题判断题简答题&#xff1a;应用题&#xff1a;算法填空题&#xff1a;算法设计题&#xff1a;(待补) 选择题 1、顺序栈 S 的 Pop(S, e)操作弹出元素 e&#xff0c;则下列(C )是正…

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

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

Yakit工具篇:专项漏洞检测的配置和使用

简介&#xff08;来自官方文档&#xff09; 专项漏洞检测是针对特定应用程序或系统进行的安全漏洞扫描技术&#xff0c;旨在检测与该应用程序或系统相关的安全漏洞。 Yakit通过对常见的中间件、CMS、框架、组件进行总结、归纳&#xff0c;并针对这些组件对其常见的高危漏洞进…

027-第三代软件开发_ComboBox

第三代软件开发_ComboBox 文章目录 第三代软件开发_ComboBox项目介绍ComboBox实际使用 关键字&#xff1a; Qt、 Qml、 ComboBox、 delegate、 Connections 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#xff08;Qt Meta-Object Language&…

试题一 (软件设计师笔记)(15分)

&#x1f600;前言 现在就是总复习试题一 &#x1f3e0;个人主页&#xff1a;尘觉主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是尘觉&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我的动力&#x1f609; 在csdn获奖荣誉: &#x1f3c6;csd…