本文首先对之前的知识进行一个简要的回顾和补充,接着继续讲解CALL、RET指令,包括由此产生的子程序设计概念
前提回顾
call、ret指令的作用
1)call、ret指令都是转移指令,它们都修改IP,或同时修改CS和IP
2)它们经常被共同实现子程序的设计
3)这一章讲解call、ret指令的原理
ret、retf指令的区别
ret:利用栈中的数据修改IP以实现近转移,先让IP等于栈顶接着SP加二(相当于pop IP
)
ret
retf:利用栈中的数据修改CS和IP以实现远转移,先让IP等于栈顶接着SP加二,然后CS等于新的栈顶,最后SP再次加二(相当于pop IP
、pop CS
;注意这里是先IP后CS可见要逆向这一过程应该先push CS
,再push IP
)
retf
call指令(call 标号)
call指令经常和ret指令配合使用,因为CPU执行call指令进行两部操作
1)将当前IP或CS和IP压入栈中
2)转移(相当于jmp指令)
call指令不能实现短转移(短转移8位位移,近转移16位位移),除此之外call指令实现转移的方法和jmp指令的原理相同(只是多了一个将CS、IP入栈)
call 标号
原理上:先让SP减二,接着把IP赋给新的栈顶,最后IP加上16位位移
16位位移 = 标号处的地址 - call指令后的第一个字节的地址(有负数用补码表示,由编译器在编译时算出)
push IP
jmp near ptr 标号
再次声明,call的转移地址不管是相对地址还是绝对地址,都只有near(16位模式下为16位,32位模式下为32位)和far(16位模式下为32位,32位模式下为48位)偏移
call指令的各种变式
call far ptr 标号:实现远转移
call指令结合far ptr实现远转移
call far ptr
相当于
push CS
push IP
jmp far ptr 标号
call 16位寄存器:同样实现近转移
16位寄存器存储16位数据
call 16位reg
这等价于
(sp) = (sp) - 2
((ss)*16 + (sp)) = (ip)
(ip) = (16位寄存器)
call word ptr 内存单元地址、call dword 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,(IP) = 0E(因为push后IP向下移动两个字节)
再看下面这段
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
call和ret(retf)的配合使用
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 ; 继续从mov bx,ax这句开始执行
code ends
end start
上面这段程序执行后bx = 8
子程序设计:参数传递
mov ds,ax
mov si,0
mov di,16
mov cx,s
s:mov bx,[si]
call cube
mov [di],ax
mov [di].2,dx
add si,2
add di,4
loop s
mov ax,2c00h
int 21h
cube:mov ax,bx
mul bx
mul bx
ret
code ends
end start
上面实现了一个计算给定参数(这里是bx)的3次方的函数(子程序)
批量数据的传递:传递多个参数
对于字符串,我们可以传递它的首地址和其长度实现对整个字符串的操作
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, 4cooh
int 21h
capital:and byte ptr [si],11011111b
inc si
loop capital
code ends
end start
P.S. 除了用寄存器传递参数,还有一种通用的方法是通过栈来传递参数
jcxz结合cx进行条件判断
规定字符串都以0字符结尾,将以下四个字符串转换为大写
(data段和前面相同,此处略,详见前)
code segement
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
顺便一提,jcxz
这个命名可以记忆为jump if cx is zero