实现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激光打印机开机后电源键、复印键一起双闪,电源键闪红灯、复印键闪绿灯; 检测维修: 根据闪灯故障判断如果无卡纸异常情况下可能是激光器故障,因为以前曾经维修过一台一模一样的机器故障基本相同,先打开机器吧,把硒鼓拿出来先看看有没有卡纸,进纸…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...

全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...