汇编语言-子程序调用

汇编语言-子程序调用

ret与ref指令

ret

ret == pop IP

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

功能介绍

retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移
CPU执行ret指令时,进行下面两步操作:

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

(2)(sp) = (sp) + 2

相当于进行:

pop IP

retf

retf == pop IP + POP CS

功能介绍

CPU执行retf指令时,进行下面两步操作:

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

相当于进行:

pop IP
pop CS

call指令

call 标号

功能介绍

(把当前IP压栈后, 转到标号处执行指令)

a. (SP) = (SP) - 2
   ((SS) * 16 + SP) = (IP)
b. (IP) = (IP) + 16位位移

相当于:

push IP
jmp near ptr 标号
  • 16位位移 = “标号”处的地址 - call指令后的第一个字节的地址;
  • 16位位移的范围 -32768—-32767, 用补码表示;
  • 16位位移由编译程序在编译时算出;

call far ptr 标号

功能介绍

(把当前CS,IP压栈后, 转到标号处执行指令)

a. (SP) = (SP) - 2
   ((SS) * 16 + SP) = (CS)
b. (SP) = (SP) - 2
   ((SS) * 16 + SP) = (IP)
c. (CS) = 标号所在段的段地址
   (IP) = 标号在段中的偏移地址

相当于:

push CS
push IP
jmp par ptr 标号

call 16位寄存器

功能介绍

(sp) = (sp) – 2
((ss) * 16 + (sp)) = (IP)
(IP) = (16位寄存器) 

相当于:

push IP 
jmp 16位寄存器

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

call dword ptr 内存单元地址

功能介绍

push CS
push IP
jmp dword 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
((IP)= ds:[0], (CS) = ds:[2])

子程序调用

通过上面介绍的两个指令,我们可以完成子程序的调用。简单调用程序如下:

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

子程序调用-传递参数问题

我们在写c语言或者其他高级语言的时候,要经常用到函数之间的参数传递这一个概念。那么在汇编语言中,我们怎么做到总程序和子程序之间的参数传递呢?

寄存器存放法

首先可以考虑在寄存器中,存放数据,比如a存放在ax中,b存放在bx中。

mov ax,a
mov bx,b

这种方式可以在参数比较少的时候使用,但是参数多了呢?那么那么多的寄存器给你存放。因此这种方式不是长久之计。

内存存放法

我们想到了一个比较好的思路,就是将参数保存到内存中,然后在寄存器中存放这些参数的首地址,通过首地址访问一系列的参数。这种方式,显然可以存放更多的数据,并且没有数量上的限制。

;参数存放段
data segment
    db 'aaaaa',0
    db 'aaaaa',0
    db 'aaaaa',0
data ends

code segment 

    ...
    ...
    mov ax,data
    mov es,ax
    mov si,0

    call sub1

    ...
    ...
    sub1:   mov ax,es[si]
            ...
            ...
    ret

code ends

这里还是存在一个问题,如果在主程序中用到了一个xx寄存器,然后在子程序中也用到了这个xx寄存器,那么当子程序返回到主程序的时,主程序中存放参数的内存地址已经没有记录了,程序出错。

内存存放法(改进)

为了解决这个问题,我们需要每次进入子程序时,将子程序中的需要用到的寄存器,push到栈中,每次退出子程序时,将相应寄存器pop出来。

子程序都应遵循下面的模式:

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.cnblogs.com/AbeDay/p/5026848.html