实现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激光打印机开机后电源键、复印键一起双闪,电源键闪红灯、复印键闪绿灯; 检测维修: 根据闪灯故障判断如果无卡纸异常情况下可能是激光器故障,因为以前曾经维修过一台一模一样的机器故障基本相同,先打开机器吧,把硒鼓拿出来先看看有没有卡纸,进纸…...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...
Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...
