实现8086虚拟机(四)——mov 和 jmp 指令解码
文章目录
- mov 指令解码
- jmp 指令解码
这篇文章举例来讲讲 mov 指令和 jmp 指令解码函数的实现,其他的指令解码函数都与这些类似。
mov 指令解码
以 mov 指令中的一类:寄存器/内存 到/从 寄存器,来详细说明解码函数的实现。
机器指令格式如下:

各字段的含义如下:
w 1 bit w=0表示数据宽度是字节,w=1表示数据宽度是字
d 1 bit d=0表示reg是源操作数,d=1表示reg是目的操作数
reg 3 bitsREG W=0 W=1000 AL AX001 CL CX010 DL DX011 BL BX100 AH SP101 CH BP110 DH SI111 BH Dl
mod 2 bits00 MemoryMode,nodisplacement follows01 MemoryMode,8-bit displacementfollows10 MemoryMode,16-bit displacementfollows11 RegisterMode(no displacement)
rm 3 bitsMOD=11 EFFECTIVE ADDRESS CALCULATIONR/M w=0 w=1 R/M MOD=00 MOD=01 MOD=10000 AL AX 000 (BX)+(SI) (BX)+(SI)+D8 (BX)+(SI)+D16001 CL CX 001 (BX)+(DI) (BX)+(DI)+D8 (BX)+(DI)+D16010 DL DX 010 (BP)+(SI) (BP)+(SI)+D8 (BP)+(SI)+D16011 BL BX 011 (BP)+(DI) (BP)+(DI)+D8 (BP)+(DI)+D16100 AH SP 100 (SI) (SI)+D8 (SI)+D16101 CH BP 101 (DI) (DI)+D8 (DI)+D16110 DH SI 110 DIRECTADDRESS (BP)+D8 (BP)+D16111 BH DI 111 (BX) (BX)+D8 (BX)+D16
解码函数 decodeMovRegOrMemoryToFromReg 的目的就是将它转换为如下形式的中间指令格式:
指令类型,指令详细类型,[源操作数],[目的操作数]
decodeMovRegOrMemoryToFromReg 函数首先检测输入的机器指令的长度:
func decodeMovRegOrMemoryToFromReg(instructions []byte) []byte {/* 0b100010dw,mod reg r/m, (DISP-LO ), (DiSP-HI)*/decodegth := len(instructions)if decodegth < 2 {return nil}dispLen := lenDisplacement(instructions[1])if decodegth < 2+dispLen {return nil}....
}
由上文机器指令格式可知,这个指令至少有 2 字节长。如果长度达到 2 字节,再调用 lenDisplacement 获取偏移量的长度。如果指令长度达不到指令格式的要求,说明不是一条完整的指令,那就返回 nil。
lenDisplacement 的实现如下:
/* mod xxx r/m, (DISP-LO ), (DiSP-HI) */
func lenDisplacement(secondByte byte) int {mod := (secondByte & 0b11000000) >> 6rm := secondByte & 0b111if mod == 0b11 {return 0}if mod == 0b00 { /* mov bx, [1]*/if rm == 0b110 {return 2}return 0}if mod == 0b01 {return 1}return 2
}
就是根据 mod 和 rm 字段的含义返回偏移量的长度。
然后就是根据 d、w、mod、rm、reg 字段的含义,确定指令详细类型和操作数,将中间形式的指令格式返回:
decodedInstructions := []byte{InstructionMov}// return 2 + dispLend := (instructions[0] & 0b10) >> 1w := instructions[0] & 0b1mod := (instructions[1] & 0b11000000) >> 6reg := (instructions[1] & 0b111000) >> 3rm := instructions[1] & 0b111switch mod {case 0b11: //RegisterMode(no displacement)if w == 0 {decodedInstructions = append(decodedInstructions, MovReg8ToReg8)} else {decodedInstructions = append(decodedInstructions, MovReg16ToReg16)}if d == 0 { //reg是源操作数decodedInstructions = append(decodedInstructions, reg)decodedInstructions = append(decodedInstructions, rm)} else {decodedInstructions = append(decodedInstructions, rm)decodedInstructions = append(decodedInstructions, reg)}default:if d == 0 {if w == 0 {decodedInstructions = append(decodedInstructions, MovReg8ToMemory)} else {decodedInstructions = append(decodedInstructions, MovReg16ToMemory)}decodedInstructions = append(decodedInstructions, reg)decodedInstructions = append(decodedInstructions,decodeMemoryOperand(mod, rm, instructions[2:])...)} else {if w == 0 {decodedInstructions = append(decodedInstructions, MovMemoryToReg8)} else {decodedInstructions = append(decodedInstructions, MovMemoryToReg16)}decodedInstructions = append(decodedInstructions,decodeMemoryOperand(mod, rm, instructions[2:])...)decodedInstructions = append(decodedInstructions, reg)}}return decodedInstructions
比如,当 mod 字段为 0b11 时,表示两个操作数都是寄存器,如果 d 为 0,那么 reg 字段就是源操作数,rm 字段就是目的操作数。如果 w 为1,那么操作数的宽度就是16位。这时候生成的中间指令格式为:
InstructionMov,MovReg16ToReg16 ,reg , rm
如果 reg 的值是 0,rm 的值是 1,这条指令的源汇编指令就是:
mov ax,cx
就是这么简单。
再看下解码 mov 立即数到内存/寄存器 解码函数 decodeMovImmediateToRegOrMemory 的实现:
func decodeMovImmediateToRegOrMemory(instructions []byte) []byte {/*1100011w, mod 000 rm, [disp-lo] [disp-hi] data [data]*/decodegth := len(instructions)if decodegth < 2 {return nil}w := instructions[0] & 0x1dispLen := lenDisplacement(instructions[1])dataLen := 1if w == 1 {dataLen = 2}if decodegth < 2+dispLen+dataLen {return nil}decodedInstructions := []byte{InstructionMov}mod := (instructions[1] & 0b11000000) >> 6rm := instructions[1] & 0b111if w == 0 {if mod == 0b11 {decodedInstructions = append(decodedInstructions, MovImmediateToReg8)decodedInstructions = append(decodedInstructions, instructions[2])decodedInstructions = append(decodedInstructions, rm)} else {decodedInstructions = append(decodedInstructions, MovImmediate8ToMemory)decodedInstructions = append(decodedInstructions, instructions[decodegth-1])decodedInstructions = append(decodedInstructions,decodeMemoryOperand(mod, rm, instructions[2:decodegth-1])...)}} else {if mod == 0b11 {decodedInstructions = append(decodedInstructions, MovImmediateToReg16)decodedInstructions = append(decodedInstructions, instructions[2])decodedInstructions = append(decodedInstructions, instructions[3])decodedInstructions = append(decodedInstructions, rm)} else {decodedInstructions = append(decodedInstructions, MovImmediate16ToMemory)decodedInstructions = append(decodedInstructions, instructions[decodegth-2])decodedInstructions = append(decodedInstructions, instructions[decodegth-1])decodedInstructions = append(decodedInstructions,decodeMemoryOperand(mod, rm, instructions[2:decodegth-2])...)}}return decodedInstructions
}
其他的都类似。
jmp 指令解码
jmp 指令包含直接转移和条件转移。
decode_jmp.go 中先把所有的指令详细类型定义出来:
const (//非条件转移JmpNotShort uint8 = iota //16位IP偏移量JmpShort //8位IP偏移量JmpDirectIntersegment //cs 16位,IP 16位JmpReg16 //IP的值在寄存器中JmpIndirectWithinsegmentJmpIndirectIntersegment//条件转移JmpJoJmpjnoJmpJbJmpJnbJmpJeJmpJneJmpJbeJmpJnbeJmpJsJmpJnsJmpJpJmpJnpJmpJlJmpJnlJmpJleJmpJnleJmpJcxz
)
初始化函数,注册所有的 jmp 指令与它的解码函数:
func init() {//jmpAddDecodeInstruction(0xE9, decodeJmpDirectWithinsegment)AddDecodeInstruction(0xEA, decodeJmpDirectIntersegment)AddDecodeInstruction(0xEB, decodeJmpDirectWithinsegmentShort)AddDecodeInstruction2(0xFF, 0b100, decodeJmpIndirectWithinsegment)AddDecodeInstruction2(0xFF, 0b101, decodeJmpIndirectIntersegment)var firstByte bytefor firstByte = 0x70; firstByte <= 0x7F; firstByte++ {AddDecodeInstruction(firstByte, decodeJmpConditional)}//jcxzAddDecodeInstruction(0xE3, decodeJmpConditional)}
以段内间接转移为例,它的机器指令格式如下:

对应的解码函数 decodeJmpIndirectWithinsegment 代码如下:
func decodeJmpIndirectWithinsegment(instructions []byte) []byte {/*11111111,mod 1 0 0 r/m,(DISP-LO ) (DISP-HI)*/decodegth := len(instructions)if decodegth < 2 {return nil}dispLen := lenDisplacement(instructions[1])if decodegth < 2+dispLen {return nil}decodedInstructions := []byte{InstructionJmp}mod := (instructions[1] & 0b11000000) >> 6rm := instructions[1] & 0b111if mod == 0b11 {decodedInstructions = append(decodedInstructions, JmpReg16)decodedInstructions = append(decodedInstructions, rm)} else {decodedInstructions = append(decodedInstructions, JmpIndirectWithinsegment)decodedInstructions = append(decodedInstructions,decodeMemoryOperand(mod, rm, instructions[2:])...)}return decodedInstructions
}
如果 mod 是 0b11,则返回的中间形式机器指令为:
InstructionJmp,JmpReg16,rm
条件转移的解码函数就更简单,因为条件转移的机器指令固定 2 个字节:

只需根据第一个字节确定详细指令类型即可,它的实现如下:
func decodeJmpConditional(instructions []byte) []byte {/*xxxxxxxx,IP-INC8*/if len(instructions) < 2 {return nil}table := map[uint8]uint8{0x70: JmpJo,0x71: Jmpjno,0x72: JmpJb,0x73: JmpJnb,0x74: JmpJe,0x75: JmpJne,0x76: JmpJbe,0x77: JmpJnbe,0x78: JmpJs,0x79: JmpJns,0x7A: JmpJp,0x7B: JmpJnp,0x7C: JmpJl,0x7D: JmpJnl,0x7E: JmpJle,0x7F: JmpJnle,0xE3: JmpJcxz,}return []byte{InstructionJmp, table[instructions[0]], instructions[1]}
}
其他指令的解码函数实现都类似。
后续文章讲解 EU 如何实现执行 mov,jmp 以及一些算数运算指令。
相关文章:
实现8086虚拟机(四)——mov 和 jmp 指令解码
文章目录mov 指令解码jmp 指令解码这篇文章举例来讲讲 mov 指令和 jmp 指令解码函数的实现,其他的指令解码函数都与这些类似。mov 指令解码 以 mov 指令中的一类:寄存器/内存 到/从 寄存器,来详细说明解码函数的实现。 机器指令格式如下&am…...
数据库技术-函数依赖、键与约束、范式
一、函数依赖 给定一个x,能唯一确定一个Y,就称x确定Y,或者说Y依赖于x,例如YX*X函数。 函数依赖又可扩展以下两种规则: 部分函数依赖:A可确定C,(A,B)也可确定C,(A,B)中的一部分(即A)可以确定C&a…...
shiro CVE-2020-1957
0x00 前言 在之前只是单纯的复现了漏洞,没有记笔记,所以补充了这篇分析笔记。 影响版本:shiro < 1.5.2 0x01 环境搭建 环境用的是:https://github.com/lenve/javaboy-code-samples/tree/master/shiro/shiro-basic 0x02 漏…...
RabbitMQ 入门到应用 ( 五 ) 基本应用
6.更多应用 6.1.AmqpAdmin 工具类 可以通过Spring的Autowired 注入 AmqpAdmin 工具类 , 通过这个工具类创建 队列, 交换机及绑定 import org.springframework.amqp.core.AmqpAdmin; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.Di…...
部署dapr的辛酸历程
前言dapr大概的了解,个人理解他就是一个分布式服务的管理,把微服务常用的组件(缓存,消息中间件、分布式锁、安全id4等)和监控以及服务注册、发现等等一系列功能以一个很抽象的方式管理起来。可能我们部署微服务用consul、ocelot、polly套件、…...
golang入门笔记——内存管理
文章目录自动内存管理概念自动内存管理-相关概念:追踪垃圾回收:分代GC(Generational GC)引用计数内存分配Go内存分配-分块Go内存分配——多级缓存Go内存管理优化Balanced GC自动内存管理 概念 1.动态内存 程序在运行时根据需求…...
97. 约数之和
Powered by:NEFU AB-IN Link 文章目录97. 约数之和题意思路代码97. 约数之和 题意 假设现在有两个自然数 A和 B,S是 A^B的所有约数之和。 请你求出 S mod 9901的值是多少。 思路 ABA^BAB的约数之和为:sumAB(1p1p12...p1Ba1)(1p2p22...p2Ba2)...sum_{A^B…...
想和20岁的自己说
男生床头千万不要放卫生纸不要叫自己的女朋友早睡,更不能叫她早起,否则有你好受的。成年人的默契:和异性单独出去旅游,如果没有明确拒绝开一间房,那基本上默认后面会发生的事情不要去考验人性,世上99%的人经…...
Unit Test and Integration Test
Unit Test and Integration Test Background It is the first time that I try to write an article in English. In the past, I didn’t write test code. Just thinking QA is responsible for testing. As a developer, I don’t need to care about tests. Although I …...
2022年全国职业院校技能大赛(中职组)网络安全竞赛试题(3)
目录 模块A 基础设施设置与安全加固 (本模块20分) 一、项目和任务描述: 假定你是某企业的网络安全工程师,对于企业的服务器系统,根据任务要求确保各服务正常运行,并通过综合运用用户安全管理与密码策略、…...
智慧城市应急指挥中心数字化及城市驾驶舱建设方案
目 录 第一章 项目概述 1.1 项目背景 1.2 项目范围 第二章 建设内容 2.1 三维可视化平台 2.1.1 多源数据接入 2.1.2 可视化编排 2.1.3 三维可视化编辑 2.1.4 空间数据可视化 2.1.5 集成框架支持 2.2 可视化场景定制开发 2.2.1 城市驾驶总舱 2.2.2 城市安全分舱 2.…...
HSCSEC 2023 个人练习
😋 大家好,我是YAy_17,是一枚爱好网安的小白。本人水平有限,欢迎各位大佬指点,欢迎关注😁,一起学习 💗 ,一起进步 ⭐ 。⭐ 此后如竟没有炬火,我便是唯一的光。…...
Android 基础知识4-2.7 RelativeLayout(相对布局)
一、RelativeLayout的概述 RelativeLayout(相对布局)是一种根据父容器和兄弟控件作为参照来确定控件位置的布局方式。在很多时候,线性布局还不能满足我们的需求,比如,我们在一行(列)上显示多个控…...
关于云计算,我们问了ChatGPT 10个问题
ChatGPT懂云计算吗?前些天,我们问了ChatGPT(非Plus收费版)一些问题。1. 什么是云计算?2. 云计算行业的护城河是什么?3. 什么是云原生?4. 微软Azure与亚马逊AWS的主要区别是什么?5. 为…...
Netty学习笔记1
Netty学习笔记(一) 在的互联网环境下,分布式系统大行其道,而分布式系统的根基在于网络编程,而 Netty 恰恰是 Java 领域网络编程的王者。如果要致力于开发高性能的服务器程序、高性能的客户端程序,必须掌握…...
RISK-V品牌的中国化历程(中)
目录 1.技术优势 出道即巅峰 2.生态布道 品牌根植中国 3.应用场景 加速品牌的商业化运作 生态布道 品牌根植中国 2015年成立非盈利组织RISC-V基金会,目前已吸引全球28个国家327家会员,包括英伟达、联发科、苹果、特斯拉、谷歌、高通、IBM、三星、麻省理…...
2023.02.19 学习周报
文章目录摘要文献阅读1.题目2.摘要3.介绍4.本文贡献5.方法5.1 Local Representation Learning5.2 Global Representation Learning5.3 Item Similarity Gating6.实验6.1 数据集6.2 结果7.结论深度学习1.对偶问题1.1 拉格朗日乘数法1.2 强对偶性2.SVM优化3.软间隔3.1 解决问题3.…...
枚举类的使用方法
一、理解枚举类型 枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。下面先来看看如何写…...
.NET3.5安装步骤及相关问题。
.NET3.5全称 Microsoft.NETFramework3.5 最新版本-.NET4.8 第一步打开控制面板 windows系统打开控制面板 选择程序 选择.NET3.5安装。 可能会出现问题。 解决方案: 报错代码80240438的常用解决办法: 方法一:检测windows update servic…...
联想M7268激光打印机开机红绿灯双闪报错不打印
故障现象: 一台联想M7268激光打印机开机后电源键、复印键一起双闪,电源键闪红灯、复印键闪绿灯; 检测维修: 根据闪灯故障判断如果无卡纸异常情况下可能是激光器故障,因为以前曾经维修过一台一模一样的机器故障基本相同,先打开机器吧,把硒鼓拿出来先看看有没有卡纸,进纸…...
汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解
一、前言 在HarmonyOS 5的应用开发模型中,featureAbility是旧版FA模型(Feature Ability)的用法,Stage模型已采用全新的应用架构,推荐使用组件化的上下文获取方式,而非依赖featureAbility。 FA大概是API7之…...
TCP/IP 网络编程 | 服务端 客户端的封装
设计模式 文章目录 设计模式一、socket.h 接口(interface)二、socket.cpp 实现(implementation)三、server.cpp 使用封装(main 函数)四、client.cpp 使用封装(main 函数)五、退出方法…...
Appium下载安装配置保姆教程(图文详解)
目录 一、Appium软件介绍 1.特点 2.工作原理 3.应用场景 二、环境准备 安装 Node.js 安装 Appium 安装 JDK 安装 Android SDK 安装Python及依赖包 三、安装教程 1.Node.js安装 1.1.下载Node 1.2.安装程序 1.3.配置npm仓储和缓存 1.4. 配置环境 1.5.测试Node.j…...
AWS vs 阿里云:功能、服务与性能对比指南
在云计算领域,Amazon Web Services (AWS) 和阿里云 (Alibaba Cloud) 是全球领先的提供商,各自在功能范围、服务生态系统、性能表现和适用场景上具有独特优势。基于提供的引用[1]-[5],我将从功能、服务和性能三个方面进行结构化对比分析&#…...
【技巧】dify前端源代码修改第一弹-增加tab页
回到目录 【技巧】dify前端源代码修改第一弹-增加tab页 尝试修改dify的前端源代码,在知识库增加一个tab页"HELLO WORLD",完成后的效果如下 [gif01] 1. 前端代码进入调试模式 参考 【部署】win10的wsl环境下启动dify的web前端服务 启动调试…...
HTML版英语学习系统
HTML版英语学习系统 这是一个完全免费、无需安装、功能完整的英语学习工具,使用HTML CSS JavaScript实现。 功能 文本朗读练习 - 输入英文文章,系统朗读帮助练习听力和发音,适合跟读练习,模仿学习;实时词典查询 - 双…...
C++参数传递 a与a的区别
在 C 中,&a(引用)和 a(值传递) 的关键区别在于 参数如何传递给函数,以及由此引发的 性能、语义和安全问题。 最核心的在于你想不想传入的参数被改变,如果想,就用参数传递&#…...
[学习笔记]使用git rebase做分支差异化同步
在一个.NET 项目中,使用了Volo.Abp库,但出于某种原因,需要源码调试,因此,使用源码方式集成的项目做了一个分支archive-abp-source 其中引用方式变更操作的提交为:7de53907 后续,在master分支中…...
Java + Spring Boot + Mybatis 插入数据后,获取自增 id 的方法
在 MyBatis 中使用 useGeneratedKeys"true" 获取新插入记录的自增 ID 值,可通过以下步骤实现: 1. 配置 Mapper XML 在插入语句的 <insert> 标签中设置: xml 复制 下载 运行 <insert id"insertUser" para…...
