《汇编语言》- 读书笔记 - 第9章 - 转移指令的原理

《汇编语言》- 读书笔记 - 第9章 - 转移指令的原理

  • 总结
  • 9.1 操作符 offset
    • 问题 9.1
  • 9.2 jmp 指令
  • 9.3 依据位移进行转移的 jmp 指令
    • jmp short 标号
      • 程序 9.1
      • 程序 9.2
        • 图 9.2 程序 9.2 的机器码
    • jmp near ptr 标号
  • 9.4 转移的目的地址在指令中的 jmp 指令
    • 如何选择 jmp short、jmp near、jmp far
  • 9.5 转移地址在寄存器中的 jmp 指令
  • 9.6 转移地址在内存中的jmp 指令
    • jmp word ptr 内存单元地址(段内转移)
    • jmp dword ptr 内存单元地址(段间转移)
    • 检测点 9.1
  • 9.7 jcxz 指令
    • 1. 循环控制
    • 2. 条件分支
    • 检测点 9.2
  • 9.8 loop 指令
    • 检测点 9.3
  • 9.9 根据位移进行转移的意义
  • 9.10 编译器对转移位移超界的检测
  • 实验 8 分析一个奇怪的程序
  • 实验 9 根据材料编程

总结

本章主要介绍了转移指令的原理,这些指令允许程序在执行过程中改变控制流程,实现程序的分支循环跳转。掌握这些概念有助于深入理解计算机程序的执行机制和内存管理。

9.1 操作符 offset

问题 9.1

在这里插入图片描述
分析:

  1. ss0 都在 CS 段,偏移量也拿到分别放在了 sidi 中。
  2. ax 通用寄存器大小1个字(2字节)够存放这条语句 mov ax, bx
  3. 先把 s 标号处的第一句复制到 ax,再从 ax 复制到 s0
assume cs:codesg 

codesg segment
	s:	mov ax, bx			; mov ax,bx 的机器码占两个字节
		mov si, offset s
		mov di, offset s0
		mov ax, cs:[si]		
		mov cs:[di], ax
	s0: nop					; nop 的机器码占一个字节
		nop
codesg ends
end s

在这里插入图片描述

9.2 jmp 指令

jmp无条件转移指令,可以只修改 IP,也可以同时修改 CS 和 IP
jmp 有两种跳转思路:

  1. 偏移量跳。向前或向后N个字节。(相对跳转)
  2. 目录地址跳。跳到指定位置。(绝对跳转)

通过只修改 偏移地址 IP 还是同时修改 段地址和偏移地址 CS:IP 可以区分段内段间转移。

中文命令说明修改的
寄存器
例子(假设有标号叫 label
段内短转移jmp short 标号根据相对偏移量在当前段内进行跳转。
IP修改范围 -128 到 +127 (8位)。
IPjmp short label
段内近转移jmp near ptr 标号根据相对偏移量在当前段内进行跳转。
IP修改范围 -32768 到 +32767(16位)。
IPjmp near ptr label
段间转移
(远转移)
jmp far ptr 标号同时修改CSIP标号段地址偏移地址
以实现跨段跳转。
CS:IPjmp far ptr label
寄存器
间接转移
jmp 寄存器指定寄存器的值加载到IP。实现修改偏移地址
(段地址CS不变)
IPjmp dx
段内
间接转移
jmp word ptr [内存]将指定内存中的数据l读取到IP
读取长度一个 word(字,16位)
IPjmp word ptr [bx+si]
段间
间接转移
jmp dword ptr [内存]将指定内存中的数据l读取到CS:IP
读取长度一个 dword(双字,32位)
IP=低16位, CS=高16位
CS:IPjmp dword ptr ds:[0]
———————————————————————————————

9.3 依据位移进行转移的 jmp 指令

使用jmp short 还是 jmp near 取决于想要跳转到的目标相对于当前指令执行位置的距离(字节)
如果该距离在 -128 ~ 127(字节)范围内,使用 jmp short
如果该距离在 -32768 ~ 32767(字节)范围内,使用 jmp near

jmp short 标号

程序 9.1

此例中因为偏移量为 03-128 ~ 127 范围内,所以使用短转移

assume cs:codesg
codesg segment
 start:	mov ax,0		; 1. 设置 ax 为 0
		jmp short s		; 2. 跳到标号 s 处
		add ax,1		; 3. 被跳过
 s:	inc ax				; 4. ax 自增 1
codesg ends				; 5. 最终执行结果 ax  为 1
end start

在这里插入图片描述
如上图查看 jmp short s 反编译的机器码对应 EB03
这里 EBJMP 指令的操作码,对应 jmp short
03就是要跳过的字节数,也就是add ax,1的机器码83C001的长度。
执行后 IP + 3 字节,跳过了 add ax,1 指向 inc ax
在这里插入图片描述
单步执行,看结果 AX=0001

程序 9.2

我们把程序 9.1 改写一下,变成下面这样:

assume cs:codesg
codesg segment
 start: mov ax,0
		mov bx,0
		jmp short s
		add ax,1
	s:	inc ax
codesg ends
end start
图 9.2 程序 9.2 的机器码

在这里插入图片描述
分析 jmp short 标号 前先回忆一下 CPU 执行指令的过程:(详见:2.10 CS和IP)

读取指令
重复
CS:IP 所指内存
指令缓冲器
IP指向下一条指令
执行当前指令

按照这个步骤,我们参照 【图 9.2】 看一下,程序 9.2 中 jmp short s 指令的读取和执行过程:

  1. (CS)=0BBDH,(IP)=0006HCS:IP 指向 EB 03( jmp short s的机器码,长度2字节);
  2. 读取指令码 EB 03 进入指令缓冲器;
  3. (IP) = (IP)+所读取指令的长度 = (IP)+2 = 0008HCS:IP 指向 add ax,1;
  4. CPU 执行指令缓冲器中的指令 EB 03;
  5. 指令 EB 03 执行后(IP+=3),(IP)=000BH,CS:IP 指向 inc ax

jmp near ptr 标号

jmp near 的偏移范围比 jmp short 更大,
如跳转的目标地址在当前代码段内,且偏移量可以使用2字节表示,则应该使用 jmp near

assume cs:codesg
codesg segment
 start:	mov ax,0		; 1. 设置 ax 为 0
		jmp near ptr s	; 2. 跳到标号 s 处
		add ax,1		; 3. 被跳过
 s:	inc ax				; 4. ax 自增 1
codesg ends				; 5. 最终执行结果 ax  为 1
end start

这段代码只是修改了 jmp near ptr s 一句。我们看下面截图:
虽然偏移量(操作数)还是 3 但此时占用了2字节
在这里插入图片描述

9.4 转移的目的地址在指令中的 jmp 指令

jmp far 它直接 修改CSIP实现跨段跳转

ssume cs:codesg
codesg segment
 start:	mov ax,0
		mov bx,0
		jmp far ptr s
		db 256 dup (0) 		; 被跳过
	 s:	add ax,1			; ax 中的值 +1
		inc ax				; ax 自增 1
codesg ends
end start

查看反汇编效果 jmp far
操作码:EA
操作数:偏移地址=010B 段地址=076C (在内存中存储,低位在前,高位在后)
在这里插入图片描述

在这里插入图片描述
单步执行,看结果 AX=0002

如何选择 jmp short、jmp near、jmp far

从设计的角度考虑,区分 jmp shortjmp nearjmp far 的主要原因是为了节省字节码空间。
根据上面的例子我们看到了:

指令机器码长度
jmp short2字节
jmp near3字节
jmp far5字节

在早期8086处理器的设计中,内存和存储器资源相对有限,因此指令大小对于程序大小和效率至关重要。
在现代处理器中,尽管内存不再是主要瓶颈,但这样的设计传统仍然保留下来,并且在某些嵌入式系统或对空间极度敏感的应用场景中仍然具有重要意义。

在实际编程中,现代汇编器会根据目标地址与当前指令之间的距离自动选择合适的跳转指令类型。程序员只需要指定目标地址,而无需关心具体的跳转指令类型。

jmp my_label
my_label:
    ; 业务代码略

当汇编器处理这段代码时,它会计算 jmp 指令与目标标签 my_label 之间的距离。

  1. 如果距离在 jmp short 指令的偏移量范围内(1 个字节),汇编器会生成 jmp short 指令。
  2. 如果距离超过 jmp short 指令的范围,但仍在 jmp near 指令的偏移量范围内(2 个字节),汇编器会生成 jmp near 指令。
  3. 如果距离超过 jmp near 指令的范围,汇编器会生成 jmp far 指令。

9.5 转移地址在寄存器中的 jmp 指令

jmp 指令将程序的控制流跳转到 BX 寄存器中存储的地址。

assume cs:codesg
codesg segment
 start:	mov bx,s	; 把标号 s 的地址存到 bx
		mov ax,0
		jmp bx		; 跳转到 bx 寄存器中存储的地址
		add ax,1	; 这句被跳过
	s:	inc ax		; ax 自增 1
codesg ends
end start

在这里插入图片描述
在这里插入图片描述
单步执行,看结果 AX=0001

9.6 转移地址在内存中的jmp 指令

jmp word ptr 内存单元地址(段内转移)

修改 IP 段内转移

assume cs:code, ds:data
data segment
    t dw offset s 		; 定义一个内存变量 t 保存标号 s 的偏移地址。
data ends

code segment
 start: mov ax, data		; 数据段偏移量存入 ax
        mov ds, ax			; 设置数据段 ds 地址
        jmp word ptr [t]	; 段内跳转,到内存变量 t 保存的址
		mov ax, 0			; 这句被跳过
     s: mov ax, 1			; 最终结果 ax 为 1
code ends
end start

在这里插入图片描述

jmp dword ptr 内存单元地址(段间转移)

同时修改 CS:IP 段间转移

assume cs:code, ds:data
data segment
    t dd 12345678h
data ends

code segment
 start: mov ax, data		; 数据段偏移量存入 ax
        mov ds, ax			; 设置数据段 ds 地址
        jmp dword ptr [t]	; 段内跳转,到内存变量 t 保存的址
code ends
end start

在这里插入图片描述
这里就确认一下 jmp 后 CSIP都正确的修改了。

检测点 9.1

《汇编语言》- 读书笔记 - 各章检测点归档 - 检测点 9.1

9.7 jcxz 指令

指令格式:jcxz 标号
jcxz(Jump if CX is Zero)指令在x86汇编语言中主要用于条件跳转,当CX寄存器的值为0时执行跳转。否则啥也不做,继续执行下一句。(所有的有条件转移指令都是短转移,对 IP 的修改范围都为: -128 ~ 127
它的常见用途包括:循环控制、条件分支

1. 循环控制

在编写循环结构时,jcxz可以用来检测循环计数器是否已经减至0,从而决定是否结束循环。
如下示例代码,循环累加 AX

assume cs:code
code segment
	start:	
		mov ax,0		; 初始化 ax 为 0
		mov cx,3		; 初始化循环次数 3 到 CX寄存器
	loop_start:
		; 循环体开始
		inc ax			; ax 自增
		; 循环体结束
		dec cx          ; 每次循环后递减CX
		jcxz loop_end   ; 如果 CX为0,则跳转到 loop_end
		jmp loop_start	; 否则跳转到 loop_start 处继续下一次循环
	loop_end:
		mov ax,4c00H
		int 21H
code ends
end start

等价于JS的 do{...}while(ax != 0)

var ax = 0, cx = 3;
do { 
    console.log(++ax);
} while (--cx);
#include <stdio.h>

int main() {
	int ax = 0, cx = 3;
    do {
        printf("Current value: %d\n", ++ax);
    } while (--cx); // cx 不等于 0 就继续
    return 0;
}

2. 条件分支

在某些算法或数据处理中,CX可能被当作条件判断的标志位。当CX代表某种条件成立与否时,可以用jcxz来进入相应的处理分支。

assume cs:code
code segment
	start:
		mov cx,0			; 将标志变量加载到 CX 寄存器中
		jcxz branch_B   	; 如果 CX 为 0,则跳转到 分支B
	branch_A:
		mov ax,6666H
		jmp branch_end		; 跳转到结束
	branch_B:
		mov ax,3333H
	branch_end:
		mov ax,4c00H
		int 21H
code ends
end start

分别演示 cx01 的效果。
在这里插入图片描述 在这里插入图片描述
总之,jcxz是一个用于简化基于CX寄存器值进行条件跳转的指令,它在需要根据CX是否为0来进行决策的场景中非常有用。不过,现代编程中往往使用高级循环结构和条件判断语句,但在底层编程和特定优化场合,jcxz仍有其独特的应用价值

检测点 9.2

《汇编语言》- 读书笔记 - 各章检测点归档 - 检测点 9.2

9.8 loop 指令

loop指令是一种条件循环指令,主要用于实现计数型循环。它依赖于CX寄存器作为循环计数器。

  1. 在执行loop指令之前,需要先将循环次数(通常是递减的次数)加载到CX寄存器中。
  2. 每次执行loop 标号时,CPU会自动将CX寄存器中的值减1
  3. 然后检查CX寄存器是否为0
    3.1. 如果CX != 0,则跳转到标号指定的地址处继续执行循环体内的代码;
    3.2. 否则CX == 0,程序流程将继续向下执行,即跳出循环。

所有的循环指令都是短转移,对 IP 的修改范围都为:-128 ~ 127
偏移量的计算方式为:标号地址 - loop 下一句的地址
以补码形式保存,在编译时获得。

loop 标号 的功能相当于:

(cx)--;
if((cx) != 0){
	jmp short 标号;
}

检测点 9.3

《汇编语言》- 读书笔记 - 各章检测点归档 - 检测点 9.3

9.9 根据位移进行转移的意义

jmp short 标号jmp near ptr 标号jcxz 标号loop 标号 这几种汇编指令,都是相对跳转。
在对应的机器码中不包含转移的目的地址,而是包含到目的地址的偏移量(字节)。
这种设计,方便了程序段在内存中的浮动装配,如:
在这里插入图片描述
这段代码中,loop s 用的相对跳转,记录的是 -4(字节) 而不是 s 的地址,这样无论这段程序加载到内存中任何位置,都能正常跳到 s
但如果使用 s 的目标地址来跳转,比如 s 的地址是2000:0000那这个地址就被限制死了。
这段程序只能加载到内存这个位置才能正常跑。如果这段内存被其它程序占用,就没法玩了。

FC说明
11111100补码
11111011反码 = 补码 - 1
10000100原码 = 反码按位取反(最左侧的符号位不动)
-4结果为 -4

9.10 编译器对转移位移超界的检测

根据位移进行相对跳转的指令,转移范围都是有限制的,
编译器会在编译时检测越界问题,如果触发会报·编译错误
所以我们只要知道偏移量是如何计算即可。实际操作中编译器在编译时会自动算好。

实验 8 分析一个奇怪的程序

《汇编语言》- 读书笔记 - 实验8 分析一个奇怪的程序

实验 9 根据材料编程

《汇编语言》- 读书笔记 - 实验9 根据材料编程

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

干货 | 实战演练基于加密接口测试测试用例设计

如果接口测试仅仅只是掌握一些requests或者其他一些功能强大的库的用法&#xff0c;是远远不够的&#xff0c;还需要具有根据公司的业务以及需求去定制化一个接口自动化测试框架能力。所以在这个部分&#xff0c;会主要介绍接口测试用例分析以及通用的流程封装是如何完成的。 首…

用HTML Canvas和JavaScript创建美丽的花朵动画效果

目录 一、程序代码 二、代码原理 三、运行效果 一、程序代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>flower</title><style>* {margin: 0;padding: 0;overflow: hidden;backg…

学习笔记20:牛客周赛32

D 统计子节点中1的个数即可&#xff08;类似树形dp&#xff1f;&#xff09; #include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<vector> #include<set> #include<map>u…

Nvidia 推出了本地版聊天 Chat with RTX;OpenAI联创Karpathy宣布离职专注个人项目

&#x1f989; AI新闻 Nvidia 推出了本地版聊天 Chat with RTX 摘要&#xff1a;英伟达最近发布了名为“Chat with RTX”的Demo版个性化AI聊天机器人&#xff0c;适用于Windows平台&#xff0c;需要Nvidia的30系/40系显卡&#xff0c;显存至少为8GB&#xff0c;系统配置包括1…

【教学类-19-05】20240214《ABAB式-规律黏贴18格-手工纸15*15CM-一页一种图案,A空,横向、边框》(中班)

背景需求 利用15*15CM手工纸制作AB色块手环&#xff08;手工纸自带色彩&#xff09; 素材准备 代码展示 作者&#xff1a;阿夏 时间&#xff1a;2024年2月14日 名称&#xff1a;正方形数字卡片AB图案 _ 华光彩云_CNKI A的位置有图案 18格 一页一种图案&#xff0c;A空&#…

步步深入 k8s 使用 pv pvc sc 在 nfs 基础上共享存储

博客原文 文章目录 前言集群环境nfs 环境搭建pod 挂载 nfs架构图 pvc 方式挂载 nfs架构图 storageclass 方式动态申请 pv架构图 参考 前言 持久化卷&#xff08;Persistent Volume, PV&#xff09;允许用户将外部存储映射到集群&#xff0c;而持久化卷申请&#xff08;Persist…

open ai api 国内配置代理指南(网上最全)

1.配置须知 open ai 作为这一波AI浪潮的推动者&#xff0c;opne ai的gpt 系列产品在使用和体验上绝对是最强大的&#xff0c;现在对于开发者来说要在代码中访问open ai api是不可用的。所以本文就主要解决这个问题。我们要了解open ai 的网站gpt的访问和api的访问收费是分开来…

K8sGPT 的使用

K8sGPT 介绍 k8sgpt 是一个扫描 Kubernetes 集群、诊断和分类问题的工具。它将 SRE 经验编入其分析器中&#xff0c;并帮助提取最相关的信息&#xff0c;通过人工智能来丰富它。它还可以与 OpenAI、Azure、Cohere、Amazon Bedrock 和本地模型结合使用。 K8sGPT Github 地址 …

波奇学Linux:文件系统

磁盘认识 磁盘被访问的基本单元是扇区-512字节。 磁盘可以看成多个同心圆&#xff0c;每个同心圆叫做磁道&#xff0c;多个扇区组成同心圆。 我们可以把磁盘看做由无数个扇区构成的存储介质。 要把数据存到磁盘&#xff0c;先定位扇区&#xff0c;用哪一个磁头&#xff0c;…

【Javascript】内存泄漏

JavaScript 内存泄露指的是在程序中&#xff0c;不再使用的内存没有被正确释放&#xff0c;导致内存占用持续增加&#xff0c;最终引发性能问题甚至崩溃。 通常哪些操作会造成内存泄漏呢&#xff1f; 未使用 var 声明的全局变量&#xff1a;在 JavaScript 中&#xff0c;如果…

Java与JavaScript的区别与联系

Java是目前编程领域使用非常广泛的编程语言&#xff0c;相较于JavaScript&#xff0c;Java更被人们熟知。很多Java程序员想学门脚本语言&#xff0c;一看JavaScript和Java这么像&#xff0c;很有亲切感&#xff0c;那干脆就学它了&#xff0c;这也间接的帮助了JavaScript的发展…

【Py/Java/C++三种语言详解】LeetCode每日一题240215【二叉树BFS】LeetCode107、二叉树的层序遍历II

有LeetCode交流群/华为OD考试扣扣交流群可加&#xff1a;948025485 可上全网独家的 欧弟OJ系统 练习华子OD、大厂真题 绿色聊天软件戳 od1336了解算法冲刺训练 文章目录 题目链接题目描述解题思路DFS和BFS异同用队列维护的BFS 代码PythonJavaC时空复杂度 相关习题华为OD算法/大…

Vue2学习第一天

Vue2 学习第一天 1. 什么是 vue? Vue 是一套用于构建用户界面的渐进式框架。 2. vue 历史 vue 是在 2013 年创建的&#xff0c;vue3 是 2020 出现的&#xff0c;现在主要是用 vue2&#xff0c;创新公司用的是 vue3 vue 的作者是尤雨溪&#xff0c;vue 的搜索热度比 react…

java的面向对象编程(oop)——认识泛型

前言&#xff1a; 打好基础&#xff0c;daydayup! 泛型 1&#xff0c;认识泛型&#xff1a; 定义类&#xff0c;接口&#xff0c;方法时&#xff0c;同时声明了一个或多个类型变量&#xff08;例&#xff1a;<E>&#xff09;,称为泛型&#xff0c;泛型接口&#xff0c;泛…

计算机网络——11EMail

EMail 电子邮件&#xff08;EMail&#xff09; 3个主要组成部分 用户代理邮件服务器简单邮件传输协议&#xff1a;SMTP 用户代理 又名“邮件阅读器”撰写、编辑和阅读邮件输入和输出邮件保存在服务器上 邮件服务器 邮箱中管理和维护发送给用户的邮件输出报文队列保持待发…

###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯

前言&#xff1a;感谢您的关注哦&#xff0c;我会持续更新编程相关知识&#xff0c;愿您在这里有所收获。如果有任何问题&#xff0c;欢迎沟通交流&#xff01;期待与您在学习编程的道路上共同进步。 目录 一. 延时函数的生成 1.通过延时计算器得到延时函数 2.可赋值改变…

2月14日作业

1.请编程实现二维数组的杨慧三角 代码&#xff1a; #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc, const char *argv[]) {int n;printf("please enter n:");scanf("%d",&n);int arr[n][n];for(…

算法——数论——快速幂

目录 快速幂 费马小定理 一、试题 算法训练 A的B的C次方次方 快速幂 快速幂是一种用于快速计算幂运算的算法。计算复杂度 O(log n)基本思想是利用指数 n 的二进制展开形式&#xff0c;将 转化为多个 a 的幂的乘积&#xff0c;然后通过迭代快速计算。 快速幂的示例代码&…

Zabbix图形中文乱码问题(显示口口)解决办法

一 切换到zabbix安装目录assets/fonts下&#xff0c;下载字体 这里使用是nginxphp作为zabbix-web展示&#xff0c;使用find 命令查找 进入目录下&#xff0c;将原有字体备份&#xff0c;下载msyh字体 wget https://www.xxshell.com/download/sh/zabbix/ttf/msyh.ttf 二 修改配…

基于FPGA的UDP实现(包含源工程文件)

1、概括 前文通过FPGA实现了ARP和ICMP协议&#xff0c;ARP协议一般用来获取目的IP地址主机的MAC地址&#xff0c;ICMP通过回显请求和回显应答来判断以太网链路是否通畅&#xff0c;这两个协议都不是用来传输用户数据的。如果用户需要向PC端传输大量数据&#xff0c;那么就必须使…
最新文章