(十)汇编语言——CALL和RET指令

这篇具有很好参考价值的文章主要介绍了(十)汇编语言——CALL和RET指令。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

(十)汇编语言——CALL和RET指令

相信大家肯定在C语言里面接触过函数这个概念,或者是一些高级语言里面的方法,那么汇编语言有没有这样类似的概念呢,答案是当然的,接下来就让我们来介绍一下汇编的模块化程序设计。

CALL指令

这个CALL指令呢,我们是第一次接触,它主要的作用就是调用子程序,实质上就是进行流程转移,而且实现转移的方法和jmp指令的原理相似。使用的格式就是call 标号,这个就类似于,把当前位置保存起来,去执行其他地方的代码,执行完了之后再回到原处去执行。就类似于我们的中断。

功能

首先将当前的IP压入栈中,然后转移到标号处执行指令。相当于下面的语句。实现的是段间转移,如果我们要实现段内转移呢?我们继续讲解。

  1. (sp)=(sp)–2
  2. ((ss)*16+(sp))=(IP)
  3. (IP)=(IP)+16位位移
push IP
jmp near ptr 标号
寄存器

而如果转移地址在寄存器里面的话,就有一点不一样了,但是和我们之前介绍的 jmp 指令类似的,我们具体看看吧!就相当于下面的语句。

push IP
jmp 16位寄存器
内存

转移地址在内存中的call指令其实也是类似的,我们给出相应的语句,相信大家可以很清楚的理解到。

;call word ptr 内存单元地址
push IP
jmp word ptr 内存单元地址

;calld word ptr 内存单元地址
push CS
push IP
jmp dword ptr 内存单元地址

段间转移

我们如果想要实现段间转移的话,使用的指令就是call far ptr 标号,这个时候,我们就会把CS和IP同时入栈保存起来。具体步骤如下所示:

  • (sp)=(sp)–2
  • ((ss)×16+(sp))=(CS)
  • (sp)=(sp)–2
  • ((ss)×16+(sp))=(IP)
  • (CS)=标号所在的段地址
  • (IP)=标号所在的偏移地址
push CS
push IP
jmp far ptr 标号

“call 标号”类似”jmp near ptr 标号”,对应的机器指令中为相对于当前IP的转移位移,而不是转移的目的地址,实现段内转移。而指令“call far ptr 标号”实现的是段间转移!

返回指令

我们这里主要介绍ret指令和retf指令。

ret

用栈中的数据,修改IP的内容,从而实现近转移,相当于 pop IP。

retf

用栈中的数据,修改CS和IP的内容,从而实现远转移;

实例

这个程序就是计算一下ax的平方,但是值得注意的就是,这里我们用到了栈段,因为我们的call 指令和ret指令需要入栈和出栈操作。

assume cs:code, ss:stack
stack segment
	db 8 dup(0)
	db 8 dup(0)
stack ends
code segment

start :mov ax, stack
	mov ss,ax
	mov sp,16
	mov ax,1000
	call s
	mov ax, 4c00h
	int 21h

  s:add ax,ax
	ret

code ends
end start

MUL指令

mul指令就是我们的乘法指令,我们之前介绍过除法div指令,我们来简单回顾一下。

被除数 AX DX和AX
除数 8位内存或寄存器 16位内存或寄存器
AL AX
余数 AH DX

而接下来我们介绍的乘法指令与这个类似,我们来看看吧!

被乘数 AL AX
除数 8位内存或寄存器 16位内存或寄存器
结果 AX DX(高位)和AX(低位)

我们来看一个例子吧!计算100乘10和100乘10000。

;100*10
mov al,100
mov bl,10
mul bl

;100*10000
mov ax,100
mov bx,10000
mul bx

模块化程序设计

我们知道,在程序设计中,模块化设计是十分重要的,那么,在汇编语言中有没有这要的设计呢?聪明的小伙伴们应该想到了,调用我们刚刚介绍的CALL 指令和RET指令即可。但是呢,需要我们去解决两个问题,那就是参数和返回值的问题,我们来看一下这两个问题我们如何来解决。

在这之前,我们来看一个问题,就是根据提供的N,计算N的3次方。我们可以考虑用循环去做,但是我们现在选择使用模块化程序设计的方法去解决,具体解决办法如下:

寄存器

把数据存储到寄存器里面是一个解决办法,我们来看看具体的操作。

  1. 首先把参数放到 bx 中,即(bx)=N;
  2. 然后子程序中用多个mul指令计算N3;
  3. 最后将结果放到dx和ax中:(dx:ax)=N3
assume cs:code
data segment
	dw 1,2,3,4,5,6,7,8
	dd 0,0,0,0,0,0,0,0
data ends

code segment
start:mov ax,data
	mov ds,ax
	mov si,0     ;指向第一组单词
	mov di,16    ;指向第二组单词
	
	mov cx,8
  s:mov bx,[si]
	call cube
	mov [di],ax
	mov [di].2,dx
	add si,2
	add di,4
	loop s        ;循环处理
	
	mov ax,4c00h
	int 21h

cube:mov ax,bx
	mul bx
	mul bx
	ret

code ends
end start

这样处理当然是没有问题的,但是会面临一个问题,就是我们的寄存器只有那么多,如果需要传递的东西比较多,寄存器不够用了怎么办,确实这是我们会遇到的问题。既然寄存器比较少,我们就换一个方法,使用内存单元去传递信息。

内存单元

为了避免寄存器不够的尴尬,于是我们现在使用内存单元去传递数据。我们的做法就是先将批量数据放到内存中,然后将它们所在内存空间的首地址放在寄存器中,传递给需要的子程序。对于具有批量数据的返回结果,也可用同样的方法。

比如我们看这个例子,将data段中的字符串转化为大写。

data segment
	db 'conversation'
data ends

我们来看一下具体的代码段怎么去写。

code segment
start:mov ax,data
	  mov ds,ax
	  mov si,0
  	  mov cx,12;循环次数
	  call capital
	  mov ax,4c00h
	  int 21h
capital: and byte ptr [si],11011111b;变大写
	  inc si
	  loop capital
	  ret
code ends

目前这样基本上是没有什么问题了。但是我们还要介绍另外一种方法,那就是通过我们的栈来实现传递参数。

接下来我们就使用栈来进行参数传递,主要的原理就是由调用者将需要传递给子程序的参数压入栈中,子程序从栈中取得参数。同样的,我们介绍一个例子,那就是计算(a–b)3,a、b为word型数据。我们来看一下代码段部分。

code segment
start:
	mov ax,1
	push ax
	mov ax,3
	push ax
	;首先将一些数据入栈
	call difcube
	;调用子程序
	mov ax,4c00h
	int 21h
	
difcube:
	push bp
	;先在栈中保存BP的值
	mov bp,sp
	mov ax,[bp+4]
	sub ax,[bp+6]
	mov bp,ax
	mul bp
	mul bp
	;获得参数并计算,返回到AX中
	pop bp
	;恢复之前的BP值
	ret 4
	;放弃入栈的参数
code ends

好啦,这就是关于用栈来传递参数的方法,我们就介绍到这。

寄存器冲突问题

接下来我们将来解决有关寄存器冲突的问题,具体来说就是避免在子程序里面使用的寄存器与主程序里面使用的寄存器冲突了,导致程序无法运行。好啦,接下来我们就会来介绍如何解决这个问题。给大家一个提示,那就是使用我们经常使用的栈来保存数据。

方法

前面说了,我们是使用栈去解决这个问题,那么就让我们来卡看具体如何使用吧。

子程序开始:

  1. 子程序中使用的寄存器入栈
  2. 子程序内容
  3. 子程序使用的寄存器出栈
  4. 返回(ret、retf)
capital:
	push cx
	push si
change:
	mov cl,[si]
	mov ch,0
	jcxz ok
	and byte ptr [si],11011111b
	inc si
	jmp short change
ok:
	pop si
	pop cx
	ret

大家看这个地方,原理就是用栈先去保存数据,然后在程序结束的时候再把原来的数据出栈。
子程序中使用的寄存器入栈
2. 子程序内容
3. 子程序使用的寄存器出栈
4. 返回(ret、retf)

capital:
	push cx
	push si
change:
	mov cl,[si]
	mov ch,0
	jcxz ok
	and byte ptr [si],11011111b
	inc si
	jmp short change
ok:
	pop si
	pop cx
	ret

大家看这个地方,原理就是用栈先去保存数据,然后在程序结束的时候再把原来的数据出栈。文章来源地址https://www.toymoban.com/news/detail-753560.html

到了这里,关于(十)汇编语言——CALL和RET指令的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 汇编语言—常见汇编指令汇总

    mov    寄存器 ,数据                如:mov ax ,8 mov   寄存器,寄存器              如:mov ax,bx mov   寄存器,内存单元          如:mov ax,[0] mov   内存单元,寄存器          如:mov [0],ax mov   段寄存器,寄存器          如:mov ds,ax add   寄存器,数据

    2024年02月10日
    浏览(40)
  • 51单片机汇编语言指令大全

    参考链接 https://blog.csdn.net/weixin_45702442/article/details/115874162 https://blog.csdn.net/acslsr/article/details/106881712 在这里稍作终结,方便复习 51系列单片机按照功能分科分为5大类 数据传送类指令28条; 算数操作类指令24条; 逻辑运算类指令25条; 控制转移类17条; 位操作类17条。 MOV A,R

    2024年02月07日
    浏览(41)
  • C语言代码的x86-64汇编指令分析过程记录

    先通过Xcode创建一个terminal APP,语言选择C。代码如下: 在return 0处打上断点,并且Xcode菜单里选择Debug|Debug Workflow|Always Show Disassembly,点击运行。这时候断点会跳到汇编代码里,得到汇编代码如下: 首先介绍下面会用到的几个寄存器: rip : 程序计数寄存器 rsp : 栈指针寄存器

    2024年02月14日
    浏览(38)
  • 用ARM进行汇编语言编程(4)带有分支的循环和条件指令执行

    ARM 编程模拟器网站地址: 在arm里也有和高级语言一样的for和while循环,可以根据条件来判断是否执行 首先我们创建一个data标签,然后在里面写一个分支,存放一些数值,然后使这些存放的数值依次相加 然后我们要将list加载到内存里 然后使用直接寻址,将r0寄存器里的值放

    2024年02月06日
    浏览(49)
  • 汇编语言实验8:BIOS/DOS功能调用与宏指令程序设计

    掌握汇编语言程序设计的基本方法和技能 掌握汇编语言源程序的编辑汇编连接和执行的完整过程 通过上机操作理解宏定义、宏调用、宏展开的概念,熟练运用宏功能编写程序 掌握BIOS/DOS基础功能的实现调用方法 理解常用的DOS功能调用的基本使用,能熟练运用1号,2号,9号,

    2024年02月03日
    浏览(63)
  • 汇编语言笔记(一)——汇编语言基础

    一、开发环境 我使用visual studio 2022 preview,其他版本的设置大同小异。 第一步: 打开visual studio,点击“创建新项目”: 第二步: visual studio并没有专门的汇编项目,所以需要挂羊头卖狗肉,选择C++空项目 第三步: 输入项目名称,点击创建 第四步: 鼠标右键单击项目名称—

    2024年02月05日
    浏览(39)
  • 5.8 汇编语言:汇编高效除法运算

    通常情况下计算除法会使用 div/idiv 这两条指令,该指令分别用于计算无符号和有符号除法运算,但除法运算所需要耗费的时间非常多,大概需要比乘法运算多消耗10倍的CPU时钟,在Debug模式下,除法运算不会被优化,但Release模式下,除法运算指令会被特定的算法经过优化后转

    2024年02月11日
    浏览(41)
  • 5.6 汇编语言:汇编高效数组寻址

    数组和指针都是用来处理内存地址的操作,二者在C语言中可以互换使用。数组是相同数据类型的一组集合,这些数据在内存中是连续存储的,在C语言中可以定义一维、二维、甚至多维数组。多维数组在内存中也是连续存储的,只是数据的组织方式不同。在汇编语言中,实现

    2024年02月11日
    浏览(43)
  • 5.7 汇编语言:汇编高效乘法运算

    乘法指令是一种在CPU中实现的基本算术操作,用于计算两个数的乘积。在汇编语言中,乘法指令通常是通过 mul(无符号乘法) 和 imul(有符号乘法) 这两个指令实现的。由于乘法指令在执行时所消耗的时钟周期较多,所以编译器在优化代码时通常会尝试将乘法操作转换为更

    2024年02月11日
    浏览(36)
  • 汇编语言第一讲:计算机的组织架构和汇编语言介绍

    第一讲:计算机的组织架构和汇编语言介绍 汇编语言 计算机组织架构 数字电路 术语回顾 数制 数字电路 硬件电路 数字电路的问题 汇编语言的开始 程序的节(sections) 调用操作系统的系统调用 列出文件(Listing files) 汇编和链接 调试汇编程序 反汇编现有的程序 附录 课程资源

    2024年04月09日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包