函数

  1. 虽然jmp/clall等命令已经可以帮助我们实现高级语言 的函数功能
  2. 但是当涉及到堆栈的数据传输时需要们自己去平衡堆栈的位置
  3. 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

  1. 这是一个编译器封装的语法糖
  2. 由编译器实现 堆栈平衡
  3. 函数的非寄存器传递参数是编译器通过 ax/eax中转实现的,这会导致ax的原始值消失

    • 可以用过push/pop来恢复ax的数据
  4. 在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

Last modification:December 17, 2018
如果觉得我的文章对你有用,请随意赞赏