函数
- 虽然
jmp/clall
等命令已经可以帮助我们实现高级语言 的函数
功能 - 但是当涉及到堆栈的数据传输时需要们自己去平衡堆栈的位置
- asm编译器提供了一套 函数语法糖可以让我们快捷的实现函数功能
调用者平栈 cdecall
; 我们在这里设置
; cs为 代码运行段
; ds为 数据段
; ss为栈空间
assume cs:main_code,ds:this_data,ss:this_stack
this_stack segment stack
stack_buf db 256 dup(0)
this_stack ends
this_data segment
str_true db 'true',10,13,'$'
str_false db 'false',10,13,'$'
this_data ends
main_code segment
; 类似main函数作为asm起始点
start:
;锁定栈
mov ax, seg this_stack
mov ss, ax
;锁定数据区
mov ax, seg this_data
mov ds,ax
; 1 保存要用到的寄存器的原始值
; 假设ax原来有数值
; 但是我们要通过ax来 传递参数/入栈
mov ax,1234h;ax原来的值
push ax;ax入栈,保存起来
; 2 通过ax,将参数压入栈
mov ax,offset str_true
push ax
; 3 跳转
call far ptr show_from_dx;远掉,push cs,ip
; 4 栈平衡
; 我们之前在栈中压入了2字节的数据作为参数传递
; 所以我们这里要把 sp 下压2字节
add sp, 2 ;调用方平衡栈
; 5 恢复ax之前的值 1234h
pop ax
; 安全退出
mov ah,4ch
int 21h
main_code ends
fx_code segment
show_from_dx:
; 1 保存我们要用到的寄存器的初始值
; 我们用bp来保存sp的初始地址
; 结束时直接恢复
push bp
; dx用于 int所以也要保存
push dx
; 2 锁定sp
mov bp, sp
; 3 取参数
; 我们压入 bp 占用了2字节
; 我们压入 dx 占用了2字节
; call 远跳转 占用了4字节
; 所以我们的参数在 bx+2+2+4
mov dx,[bp+2+2+4]
; 显示字符串
mov ah,09h
int 21h
; 4 恢复 sp的初始值
mov sp, bp
; 5 恢复 dx,bp
pop dx
pop bp
retf;远回,pop cs,ip
fx_code ends
end start
被调用者平栈 stdcall
; 我们在这里设置
; cs为 代码运行段
; ds为 数据段
; ss为栈空间
assume cs:main_code,ds:this_data,ss:this_stack
this_stack segment stack
stack_buf db 256 dup(0)
this_stack ends
this_data segment
str_true db 'true',10,13,'$'
str_false db 'false',10,13,'$'
this_data ends
main_code segment
; 类似main函数作为asm起始点
start:
;锁定栈
mov ax, seg this_stack
mov ss, ax
;锁定数据区
mov ax, seg this_data
mov ds,ax
; 1 保存要用到的寄存器的原始值
; 假设ax原来有数值
; 但是我们要通过ax来 传递参数/入栈
mov ax,1234h;ax原来的值
push ax;ax入栈,保存起来
; 2 通过ax,将参数压入栈
mov ax,offset str_true
push ax
; 3 跳转
call far ptr show_from_dx;远掉,push cs,ip
; 4 恢复ax之前的值 1234h
pop ax
; 安全退出
mov ah,4ch
int 21h
main_code ends
fx_code segment
show_from_dx:
; 1 保存我们要用到的寄存器的初始值
; 我们用bp来保存sp的初始地址
; 结束时直接恢复
push bp
; dx用于 int所以也要保存
push dx
; 2 锁定sp
mov bp, sp
; 3 取参数
; 我们压入 bp 占用了2字节
; 我们压入 dx 占用了2字节
; call 远跳转 占用了4字节
; 所以我们的参数在 bx+2+2+4
mov dx,[bp+2+2+4]
; 显示字符串
mov ah,09h
int 21h
; 4 恢复 sp的初始值
mov sp, bp
; 5 恢复 dx,bp
pop dx
pop bp
; 6
;远回,pop cs,ip
;由于我们这个函数接受了2字节的参数
;所以回到调用的地方后 sp 下压2
retf 2
fx_code ends
end start
编译器的封装 proto/proc/invoke
- 这是一个编译器封装的语法糖
- 由编译器实现 堆栈平衡
函数的非寄存器传递参数是编译器通过
ax/eax
中转实现的,这会导致ax
的原始值消失- 可以用过
push/pop
来恢复ax
的数据
- 可以用过
在invoke中使用addr伪操作符时,注意在它的前面不能用
ax/eax
,否则eax的值会被覆盖掉,当然eax在addr的后面的参数中用是可以的错误代码:
error A2133: register value overwritten by INVOKE
基本语法
proto 函数声明
proc 函数实现
invoke 调用函数
demo
; 我们在这里设置
; cs为 代码运行段
; ds为 数据段
; ss为栈空间
assume cs:main_code,ds:this_data,ss:this_stack
this_stack segment stack
stack_buf db 256 dup(0)
this_stack ends
this_data segment
str_true db 'true',10,13,'$'
str_false db 'false',10,13,'$'
this_data ends
; 1 函数申明
; 不需要申明我们要用到的寄存器
show_from_dx proto far stdcall arg1:word ,arg2:word
main_code segment
; 类似main函数作为asm起始点
start:
;锁定栈
mov ax, seg this_stack
mov ss, ax
;锁定数据区
mov ax, seg this_data
mov ds,ax
; 2 函数调用
; 2.1 保存要用到的寄存器的原始值
; 假设ax原来有数值
; 但是我们要通过ax来 传递参数/入栈
mov ax,1234h;ax原来的值
push ax;ax入栈,保存起来
; 2.2 通过ax,传递参数
mov ax,offset str_true
; 2.3 调用函数
; 我们这里传递两个参数
invoke show_from_dx,offset str_false,ax
; 2.4 恢复ax之前的值 1234h
pop ax
; 安全退出
mov ah,4ch
int 21h
main_code ends
fx_code segment
show_from_dx proc far stdcall uses dx arg1:word ,arg2:word
; 1 取参数1
mov dx,arg1
; 显示字符串
mov ah,09h
int 21h
; 2 取参数 2
mov dx,arg2
; 显示字符串
mov ah,09h
int 21h
; 2
;远回,pop cs,ip
ret
show_from_dx endp
fx_code ends
end start