当前位置:   article > 正文

《汇编语言(第四版)》王爽 第十章 CALL和RET指令_汇编语言王爽第四版 电子书

汇编语言王爽第四版 电子书

第十章 CALL和RET指令

call和ret指令都是转移指令,它们都修改IP,或者同时修改CS和IP。它们经常被用作实现子程序的设计。

10.1 ret和retf

ret指令用栈中的数据,修改IP的内容,实现近转移
retf指令用栈中的数据,修改CS和IP的内容,实现远转移

CPU在执行ret指令时,进行两步操作:

(1)(IP) = ((ss)*16+(sp)).
(2)(sp)=(sp)+2.

相当于:

pop IP.
  • 1

CPU在执行retf指令时,进行四步操作:

(1)(IP) = ((ss)*16+(sp)).
(2)(sp)=(sp)+2.
(3)(CS)=((ss)*16+(sp)).
(4)(sp)=(sp)+2.

相当于:

pop IP
pop CS.
  • 1
  • 2
assume cs:code

stack segment
 db 16 dup (0)
stack ends

code segment
	mov ax, 4c00h
	int 21
start:	mov ax, stack
	mov ss, ax
	mov sp, 16
	mov ax, 0
	push ax
	mov bx, 0
	ret
code ends

end start
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

例如这个程序,ret语句执行后,(IP)=0,CS:IP指向代码第一条指令。

10.2 call指令

CPU执行call指令时,进行两部操作:

(1)将当前的IP或CS和IP压入栈中;
(2)转移。

call不能实现短转移。

10.3 依据位移进行转移的call指令

call 标号:将当前的IP压栈后,转到标号处执行指令。

CPU执行此种形式的call指令时,进行如下操作:

(1)(sp)=(sp)-2.
(2)((ss)*16+(sp))=(IP).

相当于

push IP
Jmp near ptr 标号
  • 1
  • 2
10.4 转移的目的地址在指令中的call指令

call for ptr 标号 实现的是段间转移
CPU执行此种形式的call指令时,进行如下操作:

(1)(sp)=(sp)-2.
((ss)*16+(sp))=(CS).
(sp) = (sp)-2.
((ss)*16+(sp))=(IP).
.
(2)(CS)=标号所在段的段地址。
(IP)=标号所在段的偏移地址。

CPU执行“call for ptr 标号”时,相当于:

push CS
push IP
jmp far ptr 标号 
  • 1
  • 2
  • 3
10.5 转移地址在寄存器中的call指令

格式:
call 16位reg

功能:

(sp) = (sp) - 2
((ss)*16+(sp))=(IP)
(IP) = (16位reg)

CPU执行“call 16位reg”时,相当于:

push IP
jmp 16位reg
  • 1
  • 2
10.6 转移地址在内存中的call指令

转移地址在内存中的call指令有两种格式:

(1)call word ptr 内存单元地址

CPU执行“call word ptr 内存单元地址”时,相当于进行:

push IP
jmp word ptr 内存单元地址
  • 1
  • 2

(2)call dword ptr 内存单元地址

CPU执行“call dword ptr 内存单元地址”时,相当于进行:

push CS
push IP
jmp dword ptr 内存单元地址
  • 1
  • 2
  • 3
10.7 call和ret的配合使用
assume cs:code
code segment
start:	mov ax, 1
	mov cx, 3
	call s
	mov bx, ax
	mov ax, 4c00h
	int 21h
s:	add ax, ax
	loop s
	ret
code ends
end start
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

我们拿这个程序为例,看一下call指令和ret指令的搭配使用,程序中bx的值是8。我们发现,可以写一个具有一定功能的程序段,我们称之为子程序,在需要的时候,使用call指令转去执行,在子程序的后面使用ret指令,用栈中的数据设置IP的值,从而转到call指令后面的代码处继续执行。

10.8 mul指令

使用mul做乘法运算时,注意两点:

(1)两个数相乘,要么都是8位,要么都是16位。如果是8位,一个默认放在AL中,一个放在8位reg或内存字节单元中。如果是16位,一个放在AX中,一个放在16位reg或内存单元中。
(2)结果:8位乘法,结果放在AX中;16位乘法,结果的高位放在DX中,低位在AX中。

格式:
mul reg
mul 内存单元

内存单元可以用不同的寻址方式给出,为了熟练使用mul运算,我们来看两个例子:

(1) mul byte ptr ds:[0]

含义:(ax) = (al)*((ds)*16+(bx)+(si)+8)

(2) mul word ptr [bx+si+8]

含义:
(ax) = (ax)*((ds)16+(bx)+(si)+8)结果的低16位
(ds) = (ax)
((ds)*16+(bx)+(si)+8)结果的高16位

10.9 模块化程序设计

当问题复杂,可以设计子程序,达到一个结构性的设计求解。而call和ret提供了技术上的支持。

10.10 参数和结果传递的问题

这个问题的实质就是在探讨如何存储子程序需要的参数和产生的返回值

10.11 批量数据的传递

对于要存储多个数据的问题,我们将批量数据放在内存中,然后将它们所在的内存空间的首地址放在寄存器中,传递给需要的子程序。

例如将单词字母转为大写的程序:

assume cs:code

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
end start
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
10.12 寄存器冲突的问题

数据存放在内存中,数据的后面可以使用一个特殊的有区分的字符表示结束条件

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号