call和ret指令
1、call标号
- 将下一条指令的偏移地址入栈后
- 转到标号处执行指令
2、 ret
将栈顶的值出栈,赋值给ip
3、 call和ret联合使用的作用其实是高级语言中的函数调用
4、 实践,考虑以下几种情况
- 有无参数
- 有无返回值
- 现场保护
- 局部变量
- 堆栈平衡
call和ret
assume cs:code, ds:data, ss:stack
; 栈段
stack segment
db 100 dup(0)
stack ends
; 数据段
data segment
db 100 dup(0)
string db 'Hello!$'
data ends
; 代码段
code segment
start:
; 手动设置ds、ss的值
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
; 业务逻辑
call print
mov ax, 1122h
mov bx, 3344h
add ax, bx
; 退出
mov ax, 4c00h
int 21h
; 打印字符串
print:
; ds:dx告知字符串地址
mov dx, offset string
mov ah, 9h
int 21h
ret
code ends
end start
函数的返回值
assume cs:code, ds:data, ss:stack
; 栈段
stack segment
db 100 dup(0)
stack ends
; 数据段
data segment
a dw 0
db 100 dup(0)
string db 'Hello!$'
data ends
; 代码段
code segment
start:
; 手动设置ds、ss的值
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
; 业务逻辑
call mathFunc3
mov bx, ax
; 退出
mov ax, 4c00h
int 21h
; 返回2的3次方
; 返回值放到ax寄存器中
mathFunc3:
mov ax, 2
add ax, ax
add ax, ax
ret
; 返回2的3次方
; 返回值放到a中
mathFunc2:
mov ax, 2
add ax, ax
add ax, ax
mov a, ax
ret
; 返回2的3次方
; 返回值放到ds:0中
mathFunc1:
mov ax, 2
add ax, ax
add ax, ax
mov [0], ax
ret
code ends
end start
函数的参数
assume cs:code, ds:data, ss:stack
; 栈段
stack segment
db 100 dup(0)
stack ends
; 数据段
data segment
db 100 dup(0)
data ends
; 代码段
code segment
start:
; 手动设置ds、ss的值
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
; 业务逻辑
push 1122h
push 3344h
call sum3
add sp, 4
push 2222h
push 2222h
call sum3
add sp, 4
push 3333h
push 3333h
call sum3
add sp, 4
mov cx, 1122h
mov dx, 2233h
call sum1
mov word ptr [0], 1122h
mov word ptr [2], 2233h
call sum2
; 退出
mov ax, 4c00h
int 21h
; 返回值放ax寄存器
; 传递2个参数(放入栈中)
sum3:
; 访问栈中的参数
mov bp, sp
mov ax, ss:[bp+2]
add ax, ss:[bp+4]
ret
; 返回值放ax寄存器
; 传递2个参数(分别放ds:0、ds:2)
sum2:
mov ax, [0]
add ax, [2]
ret
; 返回值放ax寄存器
; 传递2个参数(分别放cx、dx中)
sum1:
mov ax, cx
add ax, dx
ret
code ends
end start
; 栈平衡:函数调用前后的栈顶指针要一致
; 栈如果不平衡的结果:栈空间迟早会被用完
递归调用
assume cs:code, ds:data, ss:stack
; 栈段
stack segment
db 100 dup(0)
stack ends
; 数据段
data segment
db 100 dup(0)
data ends
; 代码段
code segment
start:
; 手动设置ds、ss的值
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
; 业务逻辑
push 1122h
push 3344h
call sum
add sp, 4
push 1122h
push 3344h
call minus
; 退出
mov ax, 4c00h
int 21h
; 返回值放ax寄存器
; 传递2个参数(放入栈中)
sum:
; 访问栈中的参数
mov bp, sp
mov ax, ss:[bp+2]
add ax, ss:[bp+4]
ret
; 返回值放ax寄存器
; 传递2个参数(放入栈中)
minus:
mov bp, sp
mov ax, ss:[bp+2]
sub ax, ss:[bp+4]
ret 4
code ends
end start
; 栈平衡:函数调用前后的栈顶指针要一致
; 栈如果不平衡的结果:栈空间迟早会被用完
; 栈平衡的方法
; 1.外平栈
; push 1122h
; push 3344h
; call sum
; add sp, 4
; 2.内平栈
; ret 4
调用约定
- __cdecl: 外平栈,参数从右至左入栈
- __stdcall: 内平栈,参数从右至左入栈
- __fastcall: 内平栈,ecx,edx分别传递前面两个参数,其他参数从右至左入栈
assume cs:code, ds:data, ss:stack
; 栈段
stack segment
db 100 dup(0)
stack ends
; 数据段
data segment
db 100 dup(0)
data ends
; 代码段
code segment
start:
; 手动设置ds、ss的值
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
; 业务逻辑
push 1111h
push 2222h
push 3333h
call sum
add sp, 6
; 退出
mov ax, 4c00h
int 21h
; 返回值放ax寄存器
; 传递2个参数(放入栈中)
sum:
; 访问栈中的参数
mov bp, sp
mov ax, ss:[bp+2]
add ax, ss:[bp+4]
add ax, ss:[bp+6]
ret
code ends
end start
; 函数调用的本质
; 1.参数:push 参数值
; 2.返回值:返回值存放到ax中
; 3.栈平衡
函数的局部变量
assume cs:code, ds:data, ss:stack
; 栈段
stack segment
db 100 dup(0)
stack ends
; 数据段
data segment
db 100 dup(0)
data ends
; 代码段
code segment
start:
; 手动设置ds、ss的值
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
; 业务逻辑
push 1
push 2
call sum
add sp, 4
push 1
push 2
call sum
add sp, 4
; 退出
mov ax, 4c00h
int 21h
; 返回值放ax寄存器
; 传递2个参数(放入栈中)
sum:
; 保护bp
push bp
; 保存sp之前的值:指向bp以前的值
mov bp, sp
; 预留10个字节的空间给局部变量
sub sp, 10
; -------- 业务逻辑 - begin
; 定义2个局部变量
mov word ptr ss:[bp-2], 3
mov word ptr ss:[bp-4], 4
mov ax, ss:[bp-2]
add ax, ss:[bp-4]
mov ss:[bp-6], ax
; 访问栈中的参数
mov ax, ss:[bp+4]
add ax, ss:[bp+6]
add ax, ss:[bp-6]
; -------- 业务逻辑 - end
; 恢复sp
mov sp, bp
; 恢复bp
pop bp
ret
code ends
end start
; 函数的调用流程(内存)
; 1.push 参数
; 2.push 函数的返回地址
; 3.push bp (保留bp之前的值,方便以后恢复)
; 4.mov bp, sp (保留sp之前的值,方便以后恢复)
; 5.sub sp,空间大小 (分配空间给局部变量)
; 6.执行业务逻辑
; 7.mov sp, bp (恢复sp之前的值)
; 8.pop bp (恢复bp之前的值)
; 9.ret (将函数的返回地址出栈,执行下一条指令)
; 10.恢复栈平衡 (add sp,参数所占的空间)
函数的完整流程
assume cs:code, ds:data, ss:stack
; 栈段
stack segment
db 100 dup(0)
stack ends
; 数据段
data segment
db 100 dup(0)
data ends
; 代码段
code segment
start:
; 手动设置ds、ss的值
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
; 业务逻辑
push 1
push 2
call sum
add sp, 4
push 1
push 2
call sum
add sp, 4
; 退出
mov ax, 4c00h
int 21h
; 返回值放ax寄存器
; 传递2个参数(放入栈中)
sum:
; 保护bp
push bp
; 保存sp之前的值:指向bp以前的值
mov bp, sp
; 预留10个字节的空间给局部变量
sub sp, 10
; -------- 业务逻辑 - begin
; 定义2个局部变量
mov word ptr ss:[bp-2], 3
mov word ptr ss:[bp-4], 4
mov ax, ss:[bp-2]
add ax, ss:[bp-4]
mov ss:[bp-6], ax
; 访问栈中的参数
mov ax, ss:[bp+4]
add ax, ss:[bp+6]
add ax, ss:[bp-6]
; -------- 业务逻辑 - end
; 恢复sp
mov sp, bp
; 恢复bp
pop bp
ret
code ends
end start
; 函数的调用流程(内存)
; 1.push 参数
; 2.push 函数的返回地址
; 3.push bp (保留bp之前的值,方便以后恢复)
; 4.mov bp, sp (保留sp之前的值,方便以后恢复)
; 5.sub sp,空间大小 (分配空间给局部变量)
; 6.执行业务逻辑
; 7.mov sp, bp (恢复sp之前的值)
; 8.pop bp (恢复bp之前的值)
; 9.ret (将函数的返回地址出栈,执行下一条指令)
; 10.恢复栈平衡 (add sp,参数所占的空间)
标志寄存器
assume cs:code, ds:data, ss:stack
; 栈段
stack segment
db 100 dup(0)
stack ends
; 数据段
data segment
db 100 dup(0)
data ends
; 代码段
code segment
start:
; 手动设置ds、ss的值
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov ax, 11
mov bx, 11
sub ax, bx
; 退出
mov ax, 4c00h
int 21h
code ends
end start
网友评论