8086汇编(16位汇编)学习笔记08.函数

8086汇编(16位汇编)学习笔记08.函数-C/C++基础-断点社区-专业的老牌游戏安全技术交流社区 - BpSend.net

函数结构

函数结构的演变

函数的结构并不是随随便便就出来的而是解决了很多问题之后,大家统一认为那个结构是最好的一种方式

例如:模拟函数实现2个数相加

不用函数实现两个数相加
;这是栈段
stack segment stack
  db 512 dup(0)
stack  ends

;这是数据段
data segment

data ends

;这里是代码
CODE segment
START:
    assume ds:data
    mov ax, data
    mov ds, ax
  
  
    mov si,5H
    mov di,6H  
    add si,di

    mov si,10H
    mov di,6H  
    add si,di
  
    mov si,0A0h
    mov di,6H   
    add si,di
  
    mov ax,4C00H  ; 带参数返回的退出  
    int 21h   ;使用21号中断
   
CODE ends
end START
模拟函数实现
;这是栈段
stack segment stack
   db 512 dup(0)
stack  ends

;这是数据段
data segment

data ends

MYADD:
 add si,di
 mov ax,si

;这里是代码
CODE segment
START:
    assume ds:data
    mov ax, data
    mov ds, ax
  
  
    mov si,5H
    mov di,6H  
    jmp  MYADD

    mov si,10H
    mov di,6H  
   
  
    mov si,0A0h
    mov di,6H   
    jmp  MYADD
  
    mov ax,4C00H  ; 带参数返回的退出  
    int 21h   ;使用21号中断
   
CODE ends
end START

此时跳过去了,但函数无法返回,如果使用 jmp返回,那么其他地方调用就还是没法返回 而且无法重复的调用,那如何让他正确返回呢 , 解决办法是 jmp 跳转的地址不固定,把它存起,要跳转的时候再把它取出来, 存的地址如果用全局变量保存,可需要的变量过多,而且不确定,最好的方法是用栈保存

普通的 函数跳转与返回
;这是栈段
stack segment stack
   db 512 dup(0)
stack  ends

;这是数据段
data segment

data ends


;这里是代码
CODE segment


MYADD:
  add si,di
  mov ax,si
  ;jmp  MYADD
  pop bx     ;取出函数返回地址
  jmp bx     ;跳转到指定的地址

START:
    assume ds:data
    mov ax, data
    mov ds, ax
  
    mov si,5H
    mov di,6H 
    mov ax, MYRET
    push ax     ;将返回地址入栈 ,不直接用 push  MYRET 是不能直接push立即数,需要用寄存器中转
    jmp  MYADD  ;跳转到函数
 
MYRET: 

    mov si,10H
    mov di,6H  
    ;jmp  MYADD
    mov ax, MYRET1
    push ax     ;将返回地址入栈 ,不直接用 push  MYRET1 是不能直接push立即数,需要用寄存器中转 
    jmp  MYADD  ;跳转到函数
  
MYRET1:  

    mov si,0A0h
    mov di,6H   
    jmp  MYADD
  
    mov ax,4C00H  ; 带参数返回的退出  
    int 21h   ;使用21号中断
   
CODE ends
end START

image.png

image.png

6汇编关键字 的 函数跳转与返回

这样每次我们跳转前都需要 将返回地址入栈 , 再进行跳转, .函数返回时 需要先将地址出栈 ,再跳转.比较麻烦,因此汇编提供了相同功能的指令 call

;这是栈段
stack segment stack
   db 512 dup(0)
stack  ends

;这是数据段
data segment

data ends


;这里是代码
CODE segment


MYADD:
  add si,di
  mov ax,si
  ;jmp  MYADD
  
  ;pop bx     ;取出函数返回地址
  ;jmp bx     ;跳转到指定的地址
 
  ret    ;等同于上面注释2行代码,返回地址出栈,跳转
  

START:
    assume ds:data
    mov ax, data
    mov ds, ax
  
    mov si,5H
    mov di,6H 
     
    ;mov ax, MYRET
    ;push ax     ;将返回地址入栈 ,不直接用 push  MYRET 是不能直接push立即数,需要用寄存器中转
    ;jmp  MYADD  ;跳转到函数
;MYRET: 
  
   call  MYADD   ;等能等同于上面注释的5行代码,取地址,入栈,跳转

    mov si,10H
    mov di,6H   
      
     
    ;jmp  MYADD
    ;mov ax, MYRET1
    ;push ax     ;将返回地址入栈 ,不直接用 push  MYRET1 是不能直接push立即数,需要用寄存器中转 
    ;jmp  MYADD  ;跳转到函数   
;MYRET1:  
  
    call  MYADD    ;等能等同于上面注释的5行代码,取地址,入栈,跳转 

    mov si,0A0h
    mov di,6H   
    jmp  MYADD
  
    mov ax,4C00H  ; 带参数返回的退出 
    int 21h   ;使用21号中断
   
CODE ends
end START

img

img

img

参数

上面的 参数使用 si 和 di 传递的参数 ,但是如果参数多的话,寄存器可能不够使用,而且如果之前 si 和 di 保存了其他数据,我们需要先进行保存,用完之后再恢复,寄存器用的越多,那么使用发之前存的就越多,用完之后恢复的也就越多,所以传参尽量也不用寄存器 而是用栈,用的时候再到栈里面去拿出来

img

;这是栈段
stack segment stack
   db 512 dup(0)
stack  ends

;这是数据段
data segment

data ends


;这里是代码
CODE segment


MYADD:
 
  mov bp,sp
  mov si,[bp+2]   ;第二个参数
  mov di,[bp+4]   ;第一个参数
   
  add si,di
  mov ax,si
  ;jmp  MYADD
  
  ;pop bx     ;取出函数返回地址
  ;jmp bx     ;跳转到指定的地址
 
  ret    ;等同于上面注释2行代码,返回地址出栈,跳转
  

START:
    assume ds:data
    mov ax, data
    mov ds, ax
  

    mov si,5H
    mov di,6H 

    push  di   
    push  si   
    ;mov ax, MYRET
    ;push ax     ;将返回地址入栈 ,不直接用 push  MYRET 是不能直接push立即数,需要用寄存器中转
    ;jmp  MYADD  ;跳转到函数
;MYRET: 
  

   call  MYADD   ;等能等同于上面注释的5行代码,取地址,入栈,跳转

    mov si,10H
    mov di,6H   
  
    ;jmp  MYADD
    ;mov ax, MYRET1
    ;push ax     ;将返回地址入栈 ,不直接用 push  MYRET1 是不能直接push立即数,需要用寄存器中转 
    ;jmp  MYADD  ;跳转到函数   
;MYRET1:  
  
    call  MYADD    ;等能等同于上面注释的5行代码,取地址,入栈,跳转 

    mov si,0A0h
    mov di,6H   
    jmp  MYADD
  
    mov ax,4C00H  ; 带参数返回的退出 
    int 21h   ;使用21号中断
   
CODE ends
end START

img

此是运行结果结果是正确的,但是栈顶确实错误的,没有平栈,因此需要需要平参数的栈

  1. 由调用方平栈 调用函数后 add sp , 入栈参数总长度 cdecel c调用约定
  2. 由被调用方平栈 ret 入栈参数总长度 stdcall 标准调用约定
保存和恢复寄存器环境

当我们调用函数时,函数如果使用了我们之前保存数据的寄存器,那么函数是用之后,我们保存的数据将会丢失,因此函数在使用寄存器之前,需要先保存寄存器数据,使用之后再恢复,但是调用者并不知道函数用到了哪些寄存器,因此这件事需要函数内部来实现,但是函数自己保存寄存器环境好之后有一个问题,就是参数取值问题,解决方法是 bp,sp分离,

通过bp取参数,这样不管sp如何变化,bp的值是不变的,此时bp将无法在干其他事了,因此 bp又称为 栈帧指针

注意: ax 不需要保存,因为所有函数 ax默认当做返回值,因此任何函数都有可能修改 ax

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

游戏安全实验室_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值