实现8086虚拟机(四)——mov 和 jmp 指令解码

news2024/9/21 17:14:25

文章目录

    • mov 指令解码
    • jmp 指令解码

这篇文章举例来讲讲 mov 指令和 jmp 指令解码函数的实现,其他的指令解码函数都与这些类似。

mov 指令解码

以 mov 指令中的一类:寄存器/内存 到/从 寄存器,来详细说明解码函数的实现。

机器指令格式如下:
在这里插入图片描述
各字段的含义如下:

w 1 bit  w=0表示数据宽度是字节,w=1表示数据宽度是字
d 1 bit   d=0表示reg是源操作数,d=1表示reg是目的操作数
reg 3 bits
	REG W=0 W=1
	000 AL AX
	001 CL CX
	010 DL DX
	011 BL BX
	100 AH SP
	101 CH BP
	110 DH SI
	111 BH Dl
mod 2 bits
	00 MemoryMode,nodisplacement follows
	01 MemoryMode,8-bit displacementfollows
	10 MemoryMode,16-bit displacementfollows
	11 RegisterMode(no displacement)
rm 3 bits
	MOD=11              EFFECTIVE ADDRESS CALCULATION
	R/M w=0 w=1   R/M  MOD=00        MOD=01          MOD=10
	000 AL AX     000 (BX)+(SI)     (BX)+(SI)+D8    (BX)+(SI)+D16
	001 CL CX     001 (BX)+(DI)     (BX)+(DI)+D8    (BX)+(DI)+D16
	010 DL DX     010 (BP)+(SI)     (BP)+(SI)+D8    (BP)+(SI)+D16
	011 BL BX     011 (BP)+(DI)     (BP)+(DI)+D8    (BP)+(DI)+D16
	100 AH SP     100 (SI)          (SI)+D8         (SI)+D16
	101 CH BP     101 (DI)          (DI)+D8         (DI)+D16
	110 DH SI     110 DIRECTADDRESS (BP)+D8         (BP)+D16
	111 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) >> 6
	rm := secondByte & 0b111
	if 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 + dispLen
	d := (instructions[0] & 0b10) >> 1
	w := instructions[0] & 0b1
	mod := (instructions[1] & 0b11000000) >> 6
	reg := (instructions[1] & 0b111000) >> 3
	rm := instructions[1] & 0b111
	switch 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] & 0x1
	dispLen := lenDisplacement(instructions[1])
	dataLen := 1
	if w == 1 {
		dataLen = 2
	}

	if decodegth < 2+dispLen+dataLen {
		return nil
	}

	decodedInstructions := []byte{InstructionMov}
	mod := (instructions[1] & 0b11000000) >> 6
	rm := instructions[1] & 0b111
	if 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的值在寄存器中
	JmpIndirectWithinsegment
	JmpIndirectIntersegment
	//条件转移
	JmpJo
	Jmpjno
	JmpJb
	JmpJnb
	JmpJe
	JmpJne
	JmpJbe
	JmpJnbe
	JmpJs
	JmpJns
	JmpJp
	JmpJnp
	JmpJl
	JmpJnl
	JmpJle
	JmpJnle
	JmpJcxz
)

初始化函数,注册所有的 jmp 指令与它的解码函数:

func init() {
	//jmp
	AddDecodeInstruction(0xE9, decodeJmpDirectWithinsegment)
	AddDecodeInstruction(0xEA, decodeJmpDirectIntersegment)
	AddDecodeInstruction(0xEB, decodeJmpDirectWithinsegmentShort)
	AddDecodeInstruction2(0xFF, 0b100, decodeJmpIndirectWithinsegment)
	AddDecodeInstruction2(0xFF, 0b101, decodeJmpIndirectIntersegment)
	var firstByte byte
	for firstByte = 0x70; firstByte <= 0x7F; firstByte++ {
		AddDecodeInstruction(firstByte, decodeJmpConditional)
	}
	//jcxz
	AddDecodeInstruction(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) >> 6
	rm := instructions[1] & 0b111
	if 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 以及一些算数运算指令。

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

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

相关文章

联想M7268激光打印机开机红绿灯双闪报错不打印

故障现象: 一台联想M7268激光打印机开机后电源键、复印键一起双闪,电源键闪红灯、复印键闪绿灯; 检测维修: 根据闪灯故障判断如果无卡纸异常情况下可能是激光器故障,因为以前曾经维修过一台一模一样的机器故障基本相同,先打开机器吧,把硒鼓拿出来先看看有没有卡纸,进纸…

php小程序餐馆点餐订餐外卖系统

目录 1 绪论 1 1.1课题背景 1 1.2课题研究现状 1 1.3初步设计方法与实施方案 2 1.4本文研究内容 2 2 系统开发环境 4 2.2MyEclipse环境配置 4 2.3 B/S结构简介 4 2.4MySQL数据库 5 3 系统分析 6 3.1系统可行性分析 6 3.1.1经济可行性 6 3.1.2技术可行性 6 3.1.3运行可行性 6 …

c++11 标准模板(STL)(std::unordered_set)(二)

定义于头文件 <unordered_set> template< class Key, class Hash std::hash<Key>, class KeyEqual std::equal_to<Key>, class Allocator std::allocator<Key> > class unordered_set;(1)(C11 起)namespace pmr { templ…

python中的for循环以及枚举函数enumerate()

一、可迭代的对象&#xff08;iteratle_object&#xff09; python中可以使用for循环进行迭代的对象大致有以下几种类型&#xff1a; String(字符串)List(列表)Tuple(元组)Dictionary(字典)range()内置函数返回的对象 二、for循环迭代示例 1. 依次输出字符串"python&q…

printk浅析

内核printk原理介绍 - 知乎 (zhihu.com)34.Linux-printk分析、使用prink调试驱动 (bbsmax.com)【原创】计算机自制操作系统(Linux篇)五&#xff1a;内核开发之万丈高楼从地起---printk(理清pintf/vprintf&#xff1b;sprintf/vsprintf &#xff1b;fprintf/vfprintf) - 知乎 (z…

自抗扰控制ADRC之扩张观测器

目录 前言 1. 被控对象(被观测对象) 2.非线性观测器 2.1仿真分析 2.2仿真模型 2.3仿真结果 3.线性观测器 3.1仿真模型 3.2仿真结果 4.总结和学习问题 前言 什么叫观测器&#xff1f;为什么该类观测称为扩张观测器&#xff1f; &#xff1a;观测器可以理解为所观测…

组合数学原理与例题

目录 一、前言 二、计数原理 1、加法原理 2、分割立方体&#xff08;lanqiaoOJ题号1620&#xff09; 3、乘法原理 4、挑选子串&#xff08;lanqiaoOJ题号1621&#xff09; 5、糊涂人寄信&#xff08;lanqiaoOJ题号1622&#xff09; 6、战斗吧N皇后&#xff08;lanqiaoO…

依次判断数组1对中的每个元素是否小于等于数组2中对应位置的每个元素numpy.less_equal()

【小白从小学Python、C、Java】【计算机等级考试500强双证书】 【Python-数据分析】 依次判断数组1对中的每个元素是否 小于等于数组2中对应位置的每个元素 numpy.less_equal() [太阳]选择题 以下错误的一项是? import numpy as np a np.array([1,2,3]) b np.array([1,3,2]) …

kubernetes 核心技术-Pod(1)

概述&#xff1a; 首先要知道 Pod 不是容器&#xff01; 一、 基本概念 Pod 是 k8s 系统中可以创建和管理的最小单元。k8s 不会直接处理容器&#xff0c;而是podpod 包含多个容器(一组容器的集合)一个pod中容器共享网络命名空间pod是短暂的(生命周期) 二、Pod存在的意义 创建…

数据结构与算法总结整理(超级全的哦!)

数据结构与算法基础大O表示法时间复杂度大O表示法时间复杂度排序&#xff1a;最坏时间复杂度时间复杂度的几条基本计算规则内存工作原理什么是内存内存主要分为三种存储器随机存储器&#xff08;RAM&#xff09;只读存储器&#xff08;ROM&#xff09;高速缓存&#xff08;Cach…

玄子Share-BCSP助学手册-JAVA开发

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b2gPyAnt-1676810001349)(./assets/%E7%8E%84%E5%AD%90Share%E4%B8%89%E7%89%88.jpg)] 玄子Share-BCSP助学手册-JAVA开发 前言&#xff1a; 此文为玄子&#xff0c;复习BCSP一二期后整理的文章&#x…

多任务学习综述Multi-Task Deep Recommender Systems

Multi-Task Deep Recommender Systems: A Survey 最近看到一篇多任务学习的综述&#xff0c;觉得总结的不错&#xff0c;记录一下。 1. 简介 推荐系统天然具有多任务学习的需求&#xff0c;以视频推荐为例&#xff0c;用户具有点赞、评论、转发等不同的行为。多任务学习相比…

“生成音乐“ 【循环神经网络】

前言 本文介绍循环神经网络的进阶案例&#xff0c;通过搭建和训练一个模型&#xff0c;来对钢琴的音符进行预测&#xff0c;通过重复调用模型来进而生成一段音乐&#xff1b; 使用到Maestro的钢琴MIDI文件 &#xff0c;每个文件由不同音符组成&#xff0c;音符用三个量来表示…

千锋教育嵌入式物联网教程之系统编程篇学习-04

目录 alarm函数 raise函数 abort函数 pause函数 转折点 signal函数 可重入函数 信号集 sigemptyset() sigfillset sigismember()​ sigaddset()​ sigdelset()​ 代码讲解 信号阻塞集 sigprocmask()​ alarm函数 相当于一个闹钟&#xff0c;默认动作是终止调用alarm函数的进…

HSCSEC 2023 个人练习

&#x1f60b; 大家好&#xff0c;我是YAy_17&#xff0c;是一枚爱好网安的小白。本人水平有限&#xff0c;欢迎各位大佬指点&#xff0c;欢迎关注&#x1f601;&#xff0c;一起学习 &#x1f497; &#xff0c;一起进步 ⭐ 。⭐ 此后如竟没有炬火&#xff0c;我便是唯一的光。…

聊一聊国际化i18n

i18n 概述i18n 是国际化的缩写&#xff0c;其完整的写法是 Internationalization&#xff0c;翻译为国际化。国际化是指在软件开发中对于不同语言和地区的支持。目的是为了让一款软件可以在不同的语言和地区环境下正常运行&#xff0c;使其适应全球各地的用户。这通常包括对语言…

Simulink 自动代码生成电机控制:低阶滑模观测器仿真实现及生成代码在开发板上运行

目录 理论参考 仿真实现 运行演示 总结 前段实时搭过高阶的滑模观测器&#xff0c;相比于高阶的&#xff0c;普通的滑模观测器计算量小更适合计算能力低的MCU&#xff0c;这里参考Microchip的16位MCU所使用的观测器&#xff0c;通过Simulink建模仿真实现系统控制&#xff0…

【查看多个长图】如何方便地在安卓手机上查看多个长图?如何更便捷地浏览长图合集

经常我会看到有些知识分享是通过长图形式进行。 往往在手机本地的图片浏览器中不能很方便地查看很多长图&#xff08;能放大&#xff0c;但是横向滑动时&#xff0c;无法保证同样的放缩比例浏览同一个文件夹&#xff09;。 我推荐下面一个APP和曲折解决办法。 1、perfect vi…

Error: Timeout trying to fetch resolutions from npm

总目录&#xff1a; 如何使用VSCode插件codesight扫描出前端项目的风险依赖包并借助 npm-force-resolutions 修复之&#xff1f;blackduck issue fix 文章目录问题描述【最终解决】我搜索到的解决方案npmjs 该依赖各版本列表及对应的被下载次数github issue 说降级到0.0.3就可以…

(十五)、从插件市场引入问题反馈页面【uniapp+uinicloud多用户社区博客实战项目(完整开发文档-从零到完整项目)】

1&#xff0c;插件市场问题反馈页面 插件市场链接 dloud插件插件市场中找到问题反馈插件&#xff1a; 首先确保登录了dcloud账号。 使用hbuilderX导入插件到自己项目中。 选择合并导入。 从插件市场导入意见反馈页面的路径地址如下&#xff1a; 2&#xff0c;点击跳转到…