首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【汇编语言】call 和 ret 指令(二) —— 汇编语言应用指南:调用机制、乘法指令与模块化设计

【汇编语言】call 和 ret 指令(二) —— 汇编语言应用指南:调用机制、乘法指令与模块化设计

作者头像
Crossoads
发布2025-06-02 12:33:57
发布2025-06-02 12:33:57
23500
代码可运行
举报
文章被收录于专栏:汇编语言汇编语言
运行总次数:0
代码可运行

前言

📌 汇编语言是很多相关课程(如数据结构、操作系统、微机原理)的重要基础。但仅仅从课程的角度出发就太片面了,其实学习汇编语言可以深入理解计算机底层工作原理,提升代码效率,尤其在嵌入式系统和性能优化方面有重要作用。此外,它在逆向工程和安全领域不可或缺,帮助分析软件运行机制并增强漏洞修复能力。 本专栏的汇编语言学习章节主要是依据王爽老师的《汇编语言》来写的,和书中一样为了使学习的过程容易展开,我们采用以8086CPU为中央处理器的PC机来进行学习。

1. call 和 ret 的配合使用

前面,我们已经分别学习了ret和cal指令的原理。现在来看一下,如何将它们配合使用来实现子程序的机制。

1.1 问题引入

下面程序返回前,bx中的值是多少?

代码语言:javascript
代码运行次数:0
运行
复制
assume cs:code
code segment
	start:	mov ax,1
			mov cx,3
			call s
			mov bx,ax		;(bx)=?
			
			mov ax,4c00h
			int 2lh
		S:	add ax,ax
			loop s
			ret
code ends
end start

思考后看分析。

1.2 分析与解答

1.2.1 执行的主要过程

我们来看一下 CPU 执行这个程序的主要过程:

(1)CPU 将call s指令的机器码读入,IP指向了call s后的指令mov bx,ax,然后CPU执行call s指令,将当前的 IP值(指令mov bx,ax的偏移地址)压栈,并将 IP 的值改变为标号 s 处的偏移地址;

(2)CPU从标号 s 处开始执行指令,loop循环完毕,(ax)=8;

(3)CPU将ret指令的机器码读入,IP指向了ret 指令后的内存单元,然后CPU 执行 ret 指令 ,从栈中弹出一个值(即 call 先前压入的 mov bx,ax 指令的偏移地址)送入 IP 中。则CS:IP指向指令mov bx,ax

(4)CPU从 mov bx,ax 开始执行指令,直至完成。

1.2.2 得出结果

因此,程序返回前,(bx)=8 。我们可以看出,从标号 s 到ret的程序段的作用是计算2的N次方,计算前,N的值由CX提供。

1.3 再次分析一个程序

我们再来看下面的程序:

1.3.1 程序的主要执行过程

我们看一下程序的主要执行过程:

(1)前三条指令执行后,栈的情况如下:

(2)call 指令读入后,(IP) =000EH,CPU指令缓冲器中的代码为 B8 05 00; CPU执行B8 05 00,首先,栈中的情况变为:

然后,(IP)=(IP)+0005=0013H

(3)CPU从cs:0013H处(即标号s处)开始执行。

(4)ret指令读入后:(IP)=0016H,CPU指令缓冲器中的代码为 C3;当CPU执行C3,相当于进行pop IP,执行后,栈中的情况为:

(IP)=000EH

(5)CPU回到 cs:000EH处(即call指令后面的指令处)继续执行。

从上面的讨论中我们发现,可以写一个具有一定功能的程序段,我们称其为子程序,在需要的时候,用call指令转去执行。

可是执行完子程序后,如何让CPU接着call指令向下执行?

没错,答案就是ret。

call 指令转去执行子程序之前,call指令后面的指令的地址将存储在栈中,所以可以在子程序的后面使用 ret 指令,用栈中的数据设置IP的值,从而转到 call 指令后面的代码处继续执行。

1.4 具有子程序的源程序框架

这样,我们可以利用call和ret来实现子程序的机制。

子程序的框架如下:

具有子程序的源程序的框架如下。

现在,大家可以从子程序的角度,回过头来再看一下本节中的两个程序。

2. mul 指令

2.1 注意事项

因下面要用到,我们介绍一下mul指令,mul是乘法指令,使用 mul 做乘法的时候,注意一下两点:

(1)两个相乘的数:两个相乘的数,要么都是8位,要么都是16位。

  • 如果是8位,一个默认放在AL中,另一个放在8位reg或内存字节单元中;
  • 如果是16位,一个默认在AX中,另一个放在16位reg或内存字单元中。

(2)结果:

  • 如果是8位乘法,结果默认放在AX中;
  • 如果是16位乘法,结果高位默认在DX中存放,低位在AX中放。

2.2 格式

格式如下:

代码语言:javascript
代码运行次数:0
运行
复制
mul reg
mul 内存单元

内存单元可以用不同的寻址方式给出,比如:

(1)

代码语言:javascript
代码运行次数:0
运行
复制
mul byte ptr ds:[0]

含义为:

(ax)=(al)*((ds)*16+0);

(2)

代码语言:javascript
代码运行次数:0
运行
复制
mul word ptr [bx+si+8]

含义为:

(ax)=(al)*((ds)*16+(bx)+(si)+8)结果的低16位;

(dx)=(al)*((ds)*16+(bx)+(si)+8)结果的高16位;

2.3 示例演示

例如:

(1)计算100*10。

100和10小于255,可以做8位乘法,程序如下:

代码语言:javascript
代码运行次数:0
运行
复制
mov al,100
mov bl,10
mul bl

结果: (ax)=1000(03E8H)



(2)计算100*10000

100小于255,可10000大于255,所以必须做16位乘法,程序如下。

代码语言:javascript
代码运行次数:0
运行
复制
mov ax,100
mov bx,10000
mul bx

结果: (ax)=4240H,(dx)=000FH(F4240H=1000000)

3. 模块化程序设计

从上面我们看到,call 与 ret 指令共同支持了汇编语言编程中的模块化设计。

在实际编程中,程序的模块化是必不可少的。因为现实的问题比较复杂,对现实问题进行分析时,把它转化成为相互联系、不同层次的子问题,是必须的解决方法。

而call和ret 指令对这种分析方法提供了程序实现上的支持。

利用 call和ret指令,我们可以用简洁的方法,实现多个互相联系、功能独立的子程序来解决一个复杂的问题。

下面的内容中,我们来看一下子程序设计中的相关问题和解决方法。

4. 参数和结果传递的问题

子程序一般都要根据提供的参数处理一定的事务,处理后,将结果(返回值)提供给调用者。

其实,我们讨论参数和返回值传递的问题,实际上就是在探讨,应该如何存储子程序需要的参数和产生的返回值。

4.1 问题引入——计算N的3次方

比如,我们设计一个子程序,可以根据提供的N,来计算N的3次方。

4.1.1 两个问题

这里有两个问题:

(1)我们将参数N存储在什么地方?

(2)计算得到的数值,我们存储在什么地方?

很显然,我们可以用寄存器来存储,可以将参数放到 bx 中 ;

因为子程序中要计算 N×N×N ,可以使用多个 mul 指令,为了方便,可将结果放到 dx 和 ax中。

4.1.2 得到子程序

子程序如下。

代码语言:javascript
代码运行次数:0
运行
复制
;说明:计算N的3次方
;参数: (bx)=N
;结果: (dx:ax)=N∧3
 cube: mov ax,bx
   	   mul bx
   	   mul bx
   	   ret

注意,我们在编程的时候要注意良好的风格,对于程序应有详细的注释。

子程序的注释信息应该包含对子程序的功能、参数和结果的说明。

因为今天写的子程序,以后可能还会用到;自己写的子程序,也很可能要给别人使用,所以一定要有全面的说明。

用寄存器来存储参数和结果是最常使用的方法。对于存放参数的寄存器和存放结果的寄存器,调用者和子程序的读写操作恰恰相反

  • 调用者将参数送入参数寄存器,从结果寄存器中取到返回值;
  • 子程序从参数寄存器中取到参数,将返回值送入结果寄存器。

4.2 例题巩固

4.2.1 问题

编程:计算data段中第一组数据的 3 次方,结果保存在后面一组dword单元中。

代码语言:javascript
代码运行次数:0
运行
复制
data segment
	dw 1,2,3,4,5,6,7,8
	dd 0,0,0,0,0,0,0,0
data ends

自己独立完成后,看下面的参考代码。

4.2.2 程序实现

我们可以用到已经写好的子程序,程序如下:

代码语言:javascript
代码运行次数:0
运行
复制
code segment
	start:	mov ax,data
			mov ds,ax
			mov si,0		;ds:si指向第一组 word 单元
			mov di,16		;ds:di指向第二组 dword 单元

			mov cx,8		;循环8次
		S:	mov bx,[si]
			call cube
			mov [di],ax
			mOv [di].2,dx
			add si,2		;ds:si 指向下一个 word 单元
			add di,4		;ds:di指向下一个 dword 单元
			loop s

			mov ax,4c00h
			int 21h

cube :		mov ax,bx
			mul bx
			mul bx
			ret

code ends
end start

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-05-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 1. call 和 ret 的配合使用
    • 1.1 问题引入
    • 1.2 分析与解答
      • 1.2.1 执行的主要过程
      • 1.2.2 得出结果
    • 1.3 再次分析一个程序
      • 1.3.1 程序的主要执行过程
    • 1.4 具有子程序的源程序框架
  • 2. mul 指令
    • 2.1 注意事项
    • 2.2 格式
    • 2.3 示例演示
  • 3. 模块化程序设计
  • 4. 参数和结果传递的问题
    • 4.1 问题引入——计算N的3次方
      • 4.1.1 两个问题
      • 4.1.2 得到子程序
    • 4.2 例题巩固
      • 4.2.1 问题
      • 4.2.2 程序实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档