本人微信公众号"aeolian"~

汇编语言-10CALL和RET指令

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

ret和retf

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

《汇编语言-10CALL和RET指令》

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

《汇编语言-10CALL和RET指令》

《汇编语言-10CALL和RET指令》

call指令

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

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

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

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

call 标号(将当前的lP压栈后,转到标号处执行指令)

《汇编语言-10CALL和RET指令》

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

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

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

《汇编语言-10CALL和RET指令》

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

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

《汇编语言-10CALL和RET指令》

用汇编语法来解释此种格式的call指令

《汇编语言-10CALL和RET指令》

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

指令格式:call 16 位reg

《汇编语言-10CALL和RET指令》

 

 用汇编语法来解释此种格式的call指令

《汇编语言-10CALL和RET指令》

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

call word ptr 内存单元地址

《汇编语言-10CALL和RET指令》

例如

《汇编语言-10CALL和RET指令》

call dword ptr 内存单元地址

《汇编语言-10CALL和RET指令》

例如

《汇编语言-10CALL和RET指令》

call和ret的配合使用

call 标号会先将cs和ip压入栈然后再跳转,ret先将ip从栈中pop出然后转移到ip。

《汇编语言-10CALL和RET指令》

《汇编语言-10CALL和RET指令》

mul指令

mul是乘法指令

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

格式如下:

mul reg
mul 内存单元

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

《汇编语言-10CALL和RET指令》

 

计算100*10

《汇编语言-10CALL和RET指令》

 

计算100*10000

《汇编语言-10CALL和RET指令》

模块化程序设计

call与ret指令共同支持了汇编语言编程中的模块化设计。在实际编程中,程序的模块化是必不可少的。因为现实的问题比较复杂,对现实问题进行分析时,把它转化成为相互联系、不同层次的子问题,是必须的解决方法。而call与ret指令对这种分析方法提供了程序实现上的支持。利用call和ret指令,我们可以用简捷的方法,实现多个相互联系、功能独立的子程序来解决一个复杂的问题。

参数和结果传递的问题

子程序一般都要根据提供的参数处理一定的事务,处理后,将结果(返回值)提供给调用者。其实,参数和返回值传递的问题,实际上就是如何存储子程序需要的参数和产生的返回值的问题。

这里面就有两个问题:

(1) 将参数N存储在什么地方?
(2) 计算得到的数值,存储在什么地方?

根据提供的N,来计算N的3次方

很显然,可以用寄存器来存储,可以将参数放到bx中;因为子程序中要计算N*N*N,可以使用多个mul指令,为了方便,可将结果放到dx和ax中。

《汇编语言-10CALL和RET指令》

 

用寄存器来存储参数和结果是最常使用的方法。对于存放参数的寄存器和存放结果的寄存器,调用者和子程序的读写操作恰恰相反;调用者将参数送入参数寄存器,从结果商存器中取到返回值;子程序从参数寄存器中取到参数,将返回值送入结果寄存器。

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

《汇编语言-10CALL和RET指令》

批量数据的传递

将批量数据放到内存中,然后将它们所在内存空间的首地址放在寄存器中,传递给需要的子程序。对于具有批量数据的返回结果,也可用同样的方法。

将一个全是字母的字符串转化为大写

这个子程序需要知道两件事,字符串的内容和字符串的长度。因为字符串中的字母可能很多,所以不便将整个字符串中的所有字母都直接传递给子程序。但是,可以将字符串在内存中的首地址放在寄存器中传递给子程序。因为子程序中要用到循环,我们可以用loop指令,而循环的次数恰恰就是字符串的长度。出于方便的考虑,可以将字符串的长度放到cx中。

《汇编语言-10CALL和RET指令》

 

除了用寄存器传递参数外,还有一种通用的方法是用栈来传递参数

寄存器冲突的问题

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

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,O
      mov cx,4
      s:mov si,bx
      call capital
      add bx,5
      loop s
      mov ax,4c00h
      int 21h
      
    capital:push cx    ;先将外循环要用到的cx和si入栈
      push si
    change:mov cl,[si]
      mov ch,0
      jcxz ok
      and byte ptr [si],11011111b
      inc si
      jmp short capital
    ok:pop si    ;结束调用时从栈中把数据恢复到si和cx
      pop cx
      ret
    
code ends

end start

 

点赞

Leave a Reply

Your email address will not be published. Required fields are marked *