汇编语言-子程序调用
汇编语言-子程序调用
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