汇编学习教程:bp 寄存器

news2024/11/24 12:34:32

引言

我们在此前的学习中已经了解了CPU中众多的寄存器,比如通用寄存器 AX、BX、CX、DX,还有段寄存器 CS、DS、SS、ES。在内存访问和灵活寻址的学习中,我们重点学习了 BX 寄存器和 CX 寄存器。BX 寄存器通常配合 DS段寄存器来实现内存访问,而 CX 寄存器则配合 Loop 指令实现循环控制。

此外,除了上述我们提到的寄存器外,还有几个特殊的寄存器。IP 寄存器,全称“指令指针寄存器”,配合 CS 段寄存器使用,任意时刻 CPU 将 CS:IP 指向的地址下的数据当作代码来执行;SP 寄存器,全称“栈顶指针寄存器”,配合 SS 段寄存器使用,任意时刻 CPU 将 SS:SP 指向的地址当作栈的栈顶。

另外,我们还学习了两个 BX 寄存器的小伙伴,SI、DI。配合这两个小伙伴,BX 就能够实现多种的内存灵活寻址。

那么本篇博文,我们将介绍一个新的寄存器:BP。BP 在用法和特性上,更像是 BX 的兄弟,至于为什么这么说,相信你看完本篇博文就会明白了~那么就让我们开始本篇学习吧!

SS 的左膀右臂

我们知道,SS 段寄存器是用来存放栈段的段地址,通常情况下,它和 SP 紧密相连,共同指向了栈顶的地址。可是它并不像 CS 段寄存器那样单一,除了拥有 SP 这个左膀外,它还有一个右臂:BP。

BP

 bp 寄存器在用法上和 bx 寄存器极为相似,只不过 bx 寄存器默认绑定的段寄存器是 DS,而 bp 寄存器默认绑定的段寄存器则是:SS。首先我们先来看一下 bp 寄存器都有哪些用法和实现的功能,从中我们可以窥探出 bp 与 bx 之间的区别联系。

[bp]

做为 bx 的兄弟,bx 拥有的特性,bp 也一概不拉,bx 的值可以是一个偏移地址,bp 也行。那么首先就来说一下 [bp]:

[bp] 用来表示一处内存地址,该地址的偏移地址为 bp 寄存器中的值段地址默认为 SS 段寄存器中的值

汇编指令:

mov ax,[bp],含义为:将偏移地址为 bp 寄存器中的值,段地址为 SS 段寄存器中的值,的内存地址下的一个字数据,送入AX寄存器中。

数学化描述:(ax) = ((ss) * 16 + (bp))

[bp+idata]

做为 bx 的兄弟,你的朋友也就是我的朋友,所以 bp 也可以和 bx 的小伙伴们一起玩耍组合。例如和立即数 idata 组合进行寻址:[bp+idata]

[bp+idata] 用来表示一处内存地址,该地址的偏移地址为 bp 寄存器中的值加上一个立即数 idata段地址默认为 SS 段寄存器中的值

汇编指令:

mov ax,[bp+idata],含义为:将偏移地址为 bp 寄存器中的值加上一个立即数,段地址为 SS 段寄存器中的值,的内存地址下的一个字数据,送入AX寄存器中。

数学化描述:(ax) = ((ss) * 16 + (bp) + idata)

我们知道,[bx+idata] 寻址方式实现了类似 C语言中一维数组的数据访问,那么同样,[bp+idata] 也是实现了一维数组的访问。看到这里,你可能会意识到了,bx 存在的一系列灵活寻址,bp 是否也可以呢?答案是:当然可以!

[bp+si]

bp 当然可以和 si、di 组合,实现更加灵活的寻址:[bp+si]、[bp+di]

[bp+si] 用来表示一处内存地址,该地址的偏移地址为 bp 寄存器中的值加上 si 寄存器中的值段地址默认为 SS 段寄存器中的值

汇编指令:

mov ax,[bp+si],含义为:将偏移地址为 bp 寄存器中的值加 si 寄存器的值,段地址为 SS 段寄存器中的值,的内存地址下的一个字数据,送入AX寄存器中。

数学化描述:(ax) = ((ss) * 16 + (bp) + (si))


[bp+di] 的含义和用法与 [bp+si] 相同

同样,[bp+si] 的寻址方式实现了类似 C语言中二维数组的数据访问

[bp+si+idata]

和 bx 一样,bp 也可以和 idata、si 或者 di 进行组合,实现灵活寻址:[bp+si+idata]、[bp+di+idata]

[bp+si+idata] 用来表示一处内存地址,该地址的偏移地址为 bp 寄存器中的值加上 si 寄存器中的值,再加上一个立即数 idata段地址默认为 SS 段寄存器中的值

汇编指令:

mov ax,[bp+si+idata],含义为:将偏移地址为 bp 寄存器中的值加 si 寄存器的值再加上一个立即数 idata,段地址为 SS 段寄存器中的值,的内存地址下的一个字数据,送入AX寄存器中。

数学化描述:(ax) = ((ss) * 16 + (bp) + (si) + idata)


[bp+di+idata] 的含义和用法与 [bp+si+idata] 相同

bp 和 其他段寄存器

除了默认绑定的段寄存器:SS 外,bp 也是可以和其他段寄存器进行友好交互,当然这需要在汇编语句中使用显示指定,才可起到效果。显示指定我们之前也学习过,格式为:段寄存器:[]

bp 和 ES

mov ax,es:[bp],将偏移地址为 bp 寄存器的值段地址为 ES 寄存器的值,的地址下的一个字数据送入 AX 寄存器中

bp 和 DS

mov ax,ds:[bp],将偏移地址为 bp 寄存器的值段地址为 DS寄存器的值,的地址下的一个字数据送入 AX 寄存器中

思考

现在我们来思考博文开头说到的那个问题:为什么说 bp 是 bx 的兄弟,而不是 bx 的伙伴呢?

要想弄明白这个问题,我们需要先弄明白 bx 的伙伴定义。我们看 bx 的伙伴:idata、si、di,这三个都可以和 bx 进行组合实现灵活寻址,能够在一起玩耍,所以称呼为伙伴
那么,bp 可以和 bx 在一起玩耍吗?比如进行组合:[bx+bp],这种可以行吗?我们直接打开 debug 程序,使用 A 命令插入一条汇编语句:mov ax,[bx+bp],来看下是否通过:

 如上图所示,我们可以看到直接给报出了 Error,也就说 bx 是无法与 bp 组合搭配进行寻址的。既然无法在一起玩耍,显而易见,bp 就不是 bx 的伙伴了。

正所谓做不成伙伴就当你的兄弟,从上面的以 bp 为主实现的系列灵活寻址方式可以看出,bp 和 bx 拥有相同的定位和用法,其值都可以成为一个偏移地址,搭配对应的段寄存器实现内存数据访问,而且两者都可以和 idata、si、di 进行组合实现灵活寻址

综上所述,bp 是 bx 的兄弟,而不是 bx 的伙伴

BP 和 SP

上面我们讨论完了 bp 和 bx 两人的关系,那么接下来我们就要好好来探究下 bp 与 sp 之间还存在着什么样的火花?

SP

我们知道,sp 寄存器配合 SS 段寄存器来指向当前栈顶位置,也就是说任意时刻下 SS:SP 指向了栈顶,所以说 sp 相比 bp 是特殊的存在

通常情况下我们操作 sp,是通过一些专门的汇编指令,例如我们此前学习的进栈指令:PUSH,以及出栈指令:POP。有关这两个指令的详解,大家可以翻看此前的博文进行温习,这里不再赘述。尽管 PUSH、POP 这两个指令可以实现修改 sp 值,但是并不能做到一个绝对修改,也就是说我们实际上并不能将 sp 直接设置为一个数值,sp 值变化依靠于它上次的值,比如 PUSH 指令执行后,sp 的值就是基于上次的值减去2,POP 指令执行后,sp 的值就是基于上次的值加上2。

由于 sp 的特殊性,所以不建议在程序运行中途使用 mov 指令对 sp 进行直接赋值,因为不按规则的修改栈顶指针可能会导致栈中数据发生不可逆转的损坏,致使程序执行出现异常甚至崩溃。一般建议是在程序开始的时候申请一段栈空间,并为 SS、SP 赋值,这样是最为牢靠的。有关具体的实现细节小伙伴可翻看此前博文讲述进行温习,这里不再讨论。

两者差异

现在我们通过比较一下 BP、SP 两者之间的相同点和不同点,来寻找它们之间的联系:

相同点:

1、其值都可做为偏移地址

2、默认关联的段寄存器都是 SS 栈段寄存器

不同点:

1、两者与 SS 搭配,所指向的内存地址含义:SS:SP 指向当前栈顶SS:BP 指向了栈内某处内存空间地址

2、两者在汇编中用法不同:BP 可以与 idata、si、di 搭配实现灵活寻址,SP 则不能,需配合特定指令使用

3、两者使用场景不同:BP 用于栈内数据访问,SP 用于定位栈顶位置

4、汇编中,访问两者所指向的内存数据,方式不同:获取 BP 指向的内存数据,使用 MOV 指令即可,获取 SP 指向的内存数据,使用 POP 指令

思考

看到这里,你心中可能会出现一个疑问:有了 SP,为什么又要设计一个 BP 呢?BP 存在的意义是什么?访问栈中数据,我们 POP 也是可以的,来一个 BP 是否多此一举?

答案当然是:不是!BP 存在有着重大意义。我们先通过一个简单的小例子,来逐步深入揭开 BP 身上的面纱~

示例1

假设当前内存中栈空间如下,大小为10个字,栈顶地址为0,栈底为14H。设当前为栈满状态,即 SP 指向地址 0H。

地址数据
00001H
20002H
40003H
60004H
80005H
a0006H
c0007H
e0008H
100009H
12000AH

现在程序需要获取地址 EH 下的栈中数据【0008H】进行操作处理,问该如何实现?

针对此问题,首先就是要排除 POP 指令了,因为首先 POP 指令只能按照进栈的顺序依次取出栈中数据,示例中需要获取的数据位置,需要连续 POP 出9次才行。再者,每次 POP 出数据栈顶都会发生改变,已经 POP 出栈的数据如何存放,以及后续数据再次进行入栈问题,这些都是比较复杂的逻辑实现。所以 POP 指令显然并不适合解决该示例。

那么,最完美的解决办法就是对栈空间进行寻址~就需要使用我们的 bp 来上场了!下面我们通过具体编程实现,来熟悉 bp 的用法操作。

assume cs:code,ss:stack       ; 定义代码段、栈段

stack segment           
    dw 1,2,3,4,5,6,7,8,9,10   ; 初始化栈段内数据
stack ends                    

code segment                 
    start:                  
	    mov ax,stack          ; 获取自定义的栈段段地址,放入AX寄存器内
		mov ss,ax             ; 设置SS寄存器值为自定义的栈段段地址
		mov sp,0              ; 设置当前栈顶为0,表示栈满状态
		
		mov bp,sp             ; 将当前栈顶位置赋值给bp
		mov ax,[bp+14]        ; 使用[bp+idata]形式寻址

        mov ax,4c00H
		int 21H
code ends
end start

上述示例代码实现了题目要求,获取栈空间内指定的位置【0008H】的数据。代码是比较简洁的,首先是设置 SS 段寄存器为自定义栈空间段地址初始化 SP 值为栈满。下面就是将 sp 赋值给 bp,使用 [bp+idata] 形式进行寻址即可。

我们编译链接,将程序在 debug 中运行:

可以看到AX寄存器成功取到了栈中指定位置的数据。 

通过上述示例我们需要学习到的是,栈空间内的寻址,是相对当前栈顶位置进行偏移。示例中需要寻址的地址相对栈顶位置偏移为 +14,所以使用 [bp+14] 来表示该处内存地址。

总结

1、获取栈空间内数据,建议使用 BP 来寻址访问,比 POP 指令更加方便快捷

2、使用BP来寻址时,BP 值一般设置为当前栈顶,即 SP 的值,使用 [bp+idata] 形式进行寻址。即相对当前栈顶偏移。

那么通过该示例,我们知道了bp 的一个作用: 为访问栈段空间数据提供方便

拓展延申

我们知道程序中存在着方法调用,比如 a 方法内调用 b 方法,b 方法执行完成后重新返回到 a 方法内继续执行,在程序运行在不同方法间跳转时,那么就面临着两个问题:1、方法参数的传递;2、如何返回上层调用

在高级语言的开发中,例如 C语言、C++、Java 中,我们似乎不用考虑这些问题,方法参数怎么传递?当然是直接 方法名(参数) 就好了呀~如何返回上层调用?方法逻辑执行完毕自然就回来了,还要怎么考虑~

然而,在汇编开发中,我们则必须考虑这两个问题。因为汇编中程序员所能够使用的寄存器资源是有限的:假设你的方法只有一个参数,那么还好,我们可以把这个参数放在AX寄存器中用来承载,可是如果你的方法有好多个参数呢?五个参数,六个参数,这些个参数显然寄存器资源已经不够用了!再假设,你调用了一层方法,a() -> b(),你使用AX寄存器来保存 a() 方法的地址,这样 b() 方法执行结束后就可以拿着 AX 寄存器中的地址直接回到 a() 方法中,可是如果你进行了多层调用呢?a() 方法中调用了 b() 方法,b() 方法中又调用了 c() 方法,c() 方法中又调用了d() 方法,层层调用,很快你就发现,糟糕寄存器又不够用了!

所以,很明显宝贵的寄存器资源是不能用来充当传递方法参数的载器,和充当保留上层方法调用地址的容器。那么谁来负责处理这两个问题呢?答案就是:栈。

栈的作用

 栈的设计初衷就是为了解决上述两个问题:方法参数传递和返回上层调用

比如 a() 方法中调用了 b() 方法,b() 方法需要传入三个参数,那么我们就可以将三个参数依次 PUSH 进栈,然后将当前地址 PUSH 进栈,而后就可以进入 b() 方法内了,此时栈中数据应该是这样的:

0Ha() 方法中调用 b() 方法的指令的下一条指令地址
2Hb() 方法传入的参数 3
4Hb() 方法传入的参数 2
6Hb() 方法传入的参数 1

 这里需要解释为什么返回地址是在栈顶位置?是因为通常情况下当执行转移指令的时候,IP 寄存器的值发生改变,CPU就会执行到新的程序代码,也就是说程序就进入到了被调用的方法内。按照正常逻辑,在调用该方法之前,你需要准备好该方法所需要传入的参数,也就是说在执行转移指令之前,就需要将所需要的参数数据 PUSH 到栈中,最后再将返回地址 PUSH 进栈中,然后执行转移指令修改 IP 值,这样才是一个正确的方法调用流程。如此,你就看到了栈顶的位置是返回地址。

获取传入参数

程序进入到 b() 方法内,如何取到它的三个参数?当然是使用 bp 了!

mov bp,sp      ; 获取当前栈顶

mov ax,[bp+2]  ; 取到参数3
mov ax,[bp+4]  ; 取到参数2
mov ax,[bp+6]  ; 取到参数1

 如此通过 [bp+idata] 寻址方式,便可以获取到传入的参数数据,下面对数据进行操作处理即可。

本篇结束语

本篇主要讲解了 bp 寄存器在汇编中的功能和使用,并学习了以 bp 寄存器为主实现的多种灵活寻址方式,这些都是我们后续学习方法跳转的重要基础。

bp 寄存器的重要性不止是方便访问栈空间数据,在接下来的学习中我们再来逐步学习,现在我们先掌握使用 bp 访问栈中数据。实际上在本系列的汇编学习中,我们并不会遇到 bp 复杂使用情况,主要就是使用 bp 寻址栈段。后面如有涉及到 bp 复杂使用场景,博主在详细讲解。

感谢围观,转发分享请标明出处,谢谢~

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

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

相关文章

做完瑞吉外卖项目的一点笔记和源码

源码在 https://gitee.com/pluto8/take-out 一、软件开发整体介绍 1、软件开发流程 需求分析 :产品原型,需求规格说明书(文档形式)设计:产品文档、UI界面设计、概要设计、详细设计、数据库设计编码:项目…

STM32 实现简单定时任务调度器,支持动态添加临时任务

代码实现和硬件没关系,所以并不限于STM32,Arduino 之类的其他地方也能用,只要有一个能获取时间的函数就行,或者说,只要有一个会随着时间自动增加的变量就行,时间单位无所谓,所以确实想的话&…

菜单权限验证和分页功能

权限验证 1.创建数据库,然后测试菜单权限的联合查询语句; 2.创建项目,导入jar包,配置实体类和工具类 3.完成登录功能,当输入用户名和密码正确后跳转到框架页面 编写导航页(top.jsp)和内容页…

Vscode +Msys2配置C/C++环境

目录 前期准备:Step1: 安装Msys2Step2: 安装编译器Step3: 安装VScodeStep4: 配置VScodec_cpp_properties.jsonlaunch.jsontasks.json Step5: 创建C/C项目 前期准备: 首先,你需要下载并安装以下软件: VsCode:https://c…

软件需求分析-复习指南

这里写自定义目录标题 下面是一段用例的描述,针对一个汽车保险系统中“将一辆新车加入一个已有保单中”的用例。请你为其设计: (1) 领域模型(要求给出建立过程)(20分); (2) 活动图 (14分); (3) 顺序图 (14分…

linux内核open文件流程

打开文件流程 本文基本Linux5.15 当应用层通过open api打开一个文件,内核中究竟如何处理? 本身用来描述内核中对应open 系统调用的处理流程。 数据结构 fdtable 一个进程可以打开很多文件, 内核用fdtable来管理这些文件。 include/linu…

为项目添加 HibernateValidator

HibernateValidatorhttps://hibernate.org/validator/ 引入依赖项 首先&#xff0c;确保已将Hibernate Validator添加到Maven或Gradle依赖项中&#xff1a; <!-- Maven 依赖 --> <dependency><groupId>org.hibernate.validator</groupId><artifa…

三年功能测试经验,投了几百份简历,为什么没有收到offer?

软件测试行业3年多经验&#xff0c;学历大专自考本科&#xff0c;主要测试方向web&#xff0c;PC端&#xff0c;wap站&#xff0c;小程序公众号都测试过&#xff0c;app也测过一些&#xff0c;C端B端都有&#xff0c;除功能外&#xff0c;接口性能也有涉猎&#xff0c;但是不能…

kafka--多易杰哥讲解

Kafka是一种分布式的流式数据平台&#xff0c;广泛应用于实时流数据处理和消息系统。它可以让处理数据的应用程序能够处理高流量的数据流&#xff0c;同时提供可靠性和可扩展性。 【多易教育】-Kafka文档 1.基本概念 1.1什么是kafka Kafka 最初是由 LinkedIn 即领英公司…

教你如何用fiddler抓取https(详细教程)

对于想抓取HTTPS的测试初学者来说&#xff0c;常用的工具就是fiddler&#xff0c;可是在初学时&#xff0c;大家对于fiddler如何抓取HTTPS真是伤了脑筋&#xff0c;可能你一步步按着网上的帖子成功了&#xff0c;那当然是极好的&#xff0c;有可能没有成功&#xff0c;这时候你…

前端基础(JavaScript)——基础语法(变量,分支...) Json对象【重要】 函数定义 事件(未完待续)

目录 引出JS是啥&#xff0c;能干啥基础语法1.变量----let2.怎么打印---console3.if条件分支--啥都可以是条件例子&#xff1a;输入框&#xff0c;打印输入和未输入4.数组push 和 splice&#xff08;2&#xff09;splice&#xff0c;3个参数&#xff0c;索引开始、删除几个&…

广域网技术

广域网互连一般采用在网络层进行协议转换的方法实现。时延网关&#xff0c;更确切的说是路由器。 无连接的网际互连&#xff1a; 在网际层提供路由信息的是路由表&#xff0c;每个站或者路由器中都有一个网际路由表&#xff0c;表的每一行说明一个目标站对应的路由器地址。 路…

如何在Ubuntu20.04中配置 libarchive 库

libarchive 是什么&#xff1f; libarchive是一个开源的压缩和归档库。 它支持实时访问多种压缩文件格式&#xff0c;比如7z、zip、cpio、pax、rar、cab、uuencode等&#xff0c;因此应用十分广泛。 举个例子&#xff0c;我写了一段代码&#xff0c;想要解压一个压缩包&#…

HARVEST基音检测算法

Harvest: A high-performance fundamental frequency estimator from speech signals一种基于语音信号的高性能基频估计算法 Harvest的独特之处在于可以获得可靠的F0轮廓&#xff0c;减少了将浊音部分错误地识别为清音部分的错误。 它包括两个步骤:估计F0候选点和在这些候选点…

17JS08——函数

函数 一、函数的概念二、函数的使用2.1 声明函数2.2 调用函数2.3 函数的封装 三、函数的参数3.1 形参和实参3.2 形参和实参个数不匹配问题3.3 小结 四、函数的返回值4.1 return语句4.2 return终止函数4.3 break、continue、return的区别4.4 案例 五、arguments的使用案例1&…

案例30:基于Springboot酒店管理系统开题报告设计

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

Java-多线程解析1

一、线程的描述&#xff1a; 1、线程是一个应用程序进程中不同的执行路径比例如&#xff1a;一个WEB服务器&#xff0c;能够为多个用户同时提供请求服务&#xff1b;而 -> 进程是操作系统中正在执行的不同的应用程序,比如&#xff1a;我们可以同时打开系统的word和游戏 2、多…

Tomcat优化及Nginx、tomcat动静分离配置

Tomcat优化及Nginx、tomcat动静分离配置 一、Tomcat优化1、操作系统优化&#xff08;内核参数优化&#xff09;2、Tomacat配置文件优化3、Java虚拟机&#xff08;JVM&#xff09;调优 二、Nginx、tomcat动静分离配置(七层代理)三、四层代理 一、Tomcat优化 Tomcat默认安装下的…

八、进程等待

文章目录 一、进程创建&#xff08;一&#xff09;fork函数概念1.概念2.父子进程共享fork之前和fork之后的所有代码&#xff0c;只不过子进程只能执行fork之后的&#xff01; &#xff08;二&#xff09;fork之后&#xff0c;操作系统做了什么?1.进程具有独立性&#xff0c;代…

(二)CSharp-字段-属性-常量

一、字段 什么是字段 字段&#xff08;filed&#xff09;是一种表示与对象或类型&#xff08;类或结构体&#xff09;关联的变量字段是类型的成员&#xff0c;旧称“成员变量”与对象关联的字段亦称“实例字段”与类型关联的字段称为“静态字段”&#xff0c;由 static 修饰 …