0


汇编语言——第10章 CALL和RET指令

引言

call和ret指令都是转移指令,他们都修改IP,或同时修改CS和IP。

它们经常被共同用来实现子程序的设计。

10.1 ret和retf

ret指令用栈中的数据来修改IP****的内容,从而实现近转移。

CPU执行ret指令时:

1、(IP)=((ss)*16+(sp)),指向栈顶

2、(sp)=(sp)+2

retf指令用栈中的数据,修改CSIP****的内容,从而实现远转移。

CPU执行retf指令时:

1、(IP)=((ss)*16+(sp))

2、(sp)=(sp)+2

3、(CS)=((ss)*16+(sp))

4、(sp)=(sp)+2

用汇编的语法来解释ret和retf指令:

CPU执行ret指令相当于进行:pop IP

CPU执行retf指令相当于进行:pop IP和pop CS

assume cs:code

stack segment

db 16 dup(0)

stack ends

code segment

          mov ax,4c00h

          int 21h

start: mov ax,stack

          mov ss,ax

          mov sp,16

          mov ax,0

          push ax

          mov bx,0

          ret

code ends

end start

ret指令执行后,(IP)=0,CS:IP指向代码段的第一条命令。

assume cs:code

stack segment

db 16 dup(0)

stack ends

code segment

          mov ax,4c00h

          int 21h

start: mov ax,stack

          mov ss,ax

          mov sp,16

          mov ax,0

          push cs

          push ax

          mov bx,0

          retf

codesg ends

end start

retf指令执行后,CS:IP指向代码段的第一条命令。

检测点10.1

10.2 call指令

call指令经常跟ret指令配合使用,CPU执行call指令时:

1、将当前的IP或者CS和IP压入栈;

2、转移(jmp)。

call指令除了不能实现短转移之外,call指令实现转移的方法和jmp指令的原理相同。

call指令实现段间的转移(远转移)或近转移。

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

call标号(将当前的IP压入栈后转到目标处执行指令)

call执行此种格式的call指令时,进行以下操作:

(1)(SP)=(SP)-2
((SS)*16+(SP))=(IP)

(2)(IP)=(IP)+16位位移;

16位位移=标号处的地址-call指令后的第一个字节的地址。

16位位移的范围是-32768~32767,用补码表示。

16位位移由编译程序在编译时算出。

用汇编语法解释call指令:

push IP

jmp near ptr 标号

检测点10.2

10.4 转移的目的地址在指令中的call指令

“call far ptr 标号”实现的是段间转移。

call执行此种格式的call指令时,进行以下操作:

(1)(sp)=(sp)-2

       ((ss)*16+(sp))=(CS)

     (sp)=(sp)-2

       ((ss)*16+(sp))=(IP)

(2)(CS)=标号所在段的段地址

   (IP)=标号所在段的偏移地址

用汇编语法解释call指令:

push CS

push IP

jmp far ptr 标号

检测点10.3

10.5 转移地址在寄存器中的call指令

指令格式是:call 16位reg

功能:

(1)(sp)=(sp)-2

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

(3)(IP)=(16位reg)

用汇编语法解释此种call指令,CPU执行“call 16位reg”时,相当于:

push IP

jmp 16位reg

检测点10.4

10.6 转移地址在内存中的call指令

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

(1)call word ptr 内存单元地址;段内跳转

用汇编语法解释call word ptr 内存单元地址:

   push IP

   jmp word ptr 内存单元地址

例子:

mov sp,10h

mov ax,0123H

mov ds:[0],ax

call word ptr ds:[0]

执行后,(IP)=0123H,(sp)=0EH.

(2) call dword ptr 内存单元地址;段间跳转

     用汇编语法解释call dword ptr 内存单元地址

   push CS

   push IP

   jmp word ptr 内存单元地址

     例子:

mov sp,10h

mov ax,0123H

mov ds:[0],ax

mov word ptr ds:[2],0

call dword ptr ds:[0]

执行后,(CS)=0,(IP)=0123H,(SP)=0CH。

检测点 10.5

10.7 call和ret的配合使用

10.8 mul指令

mul是乘法指令,使用mul做乘法的时候应注意以下两点:

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

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

格式:

mull reg

mull 内存单元

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

mull byte ptr ds:[0]

含义:(ax)=(al)*((ds)*16+0);

mull word ptr [bx+si+idata]

含义:(ax)=(ax)*((ds)*16+(bx)+(si)+idata)结果的低6位。

(dx)=(ax)*((ds)*16+(bx)+(si)+idata)结果的高6位。

例子:

(1)计算100*10

两个数都小于255,可以做8位乘法

mov al,100

mov bl,10

mul bl

结果(ax)=1000(03E8H)

(2)计算100*1000

1000都大于255,要做16位乘法

mov ax,100

mov bx,10000

mul bx

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

10.9 模块化程序设计

cal和ret指令共同支持汇编语言编程中的模块化设计。

10.10 参数和结果传递的问题

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

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

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

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

assume cs:code

data segment

 dw 1,2,3,4,5,6,7,8

 dd 8 dup (0)

data ends

code segment

start: mov ax,data

          mov ds,ax

          mov si,0                               ;ds:si指向第一组word单元

          mov di,16                             ;ds:di指向第二组dword单元

          mov cx,8

      s: mov bx,[si]

          call cube

          mov [di],ax

          mov [di].2,dx

          add si,2                                ;ds:di指向下一个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

10.11 批量数据的传递

将批量数据放在内存中,然后将它们所在内存空间的首地址放在寄存器中,传递给需要的子程序,批量数据的返回结果也是采用同样的方法。除此之外还可以用栈来传递参数。

编程:将data段中的字符串转化为大写

assume cs:code

data segment

db 'conversation'

data ends

code segment

start: mov ax,data

         mov ds,ax

         mov si,0                ;ds:si指向字符串(批量数据)所在空间的首地址

         mov cx,12             ;cx存放字符串的长度

         call capital

         mov ax,4c00h

         int 21h

capital: add byte ptr [si],11011111B

         inc si

         loop capital

         ret

code ends

end start

10.12 寄存器冲突的问题

编程:将一个全是字母,以0结尾的字符串转化为大写

capital: mov cl,[si] ;低8位

        mov ch,0                                 ;高8位设置为0

        jcxz ok                                     ;如果(cx)=0则结束,如果不是0则处理

        and byte ptr [si],11011111B

        inc si

        jmp short capital

   ok: ret

编程:将data段中的字符串全部转化为大写

assume cs:code

data segment

db  'word',0

db  'unix',0

db  'wind',0

db  'good',0

data ends

此程序有bug,cx有问题

assume cs:code

data segment

db  'word',0

db  'unix',0

db  'wind',0

db  'good',0

data ends

code segment

start: mov ax,data

         mov ds,ax

         mov bx,0

         mov cx,4

    s:  mov si,bx

         call capital

         add bx,5

         loop s

         mov ax,4c00h

         int 21h

capital: mov cl,[si]

          mov ch,0

          jcxz ok

          and byte ptr [si],11011111b

          inc si

          jmp short capital

    ok: ret

code ends

end start


本文转载自: https://blog.csdn.net/m0_65159351/article/details/128642734
版权归原作者 关了个尔 所有, 如有侵权,请联系我们删除。

“汇编语言——第10章 CALL和RET指令”的评论:

还没有评论