汇编学习笔记-函数总结(8086)

汇编函数结构

  • 函数外传递函数参数(push ss 或者 直接传寄存器)
  • 保护bp:push bp
  • 用bp记录原先sp的位置
  • 在生成局部变量之间,应该先提升sp,用来开辟一段函数空间,然后把所有的局部变量入栈
  • 保护寄存器:push通用寄存器
  • --------- 业务逻辑代码 开始 ---------
  • 定义局部变量:通过“bp-位数”入栈局部变量
  • 获得局部变量:通过“bp-位数”来获得
  • 获得参数:通过“bp+位数”来获得
  • 将返回值赋给ax
  • --------- 业务逻辑代码 结束 ---------
  • 恢复通用寄存器:pop的顺序和push顺序相反,因为ax是存储返回值的,所以不用pop ax寄存器
  • 恢复sp,达到销毁局部变量的效果
  • 恢复bp:pop bp
  • ret 内平栈

整个函数是栈中执行的 所以先push的会后pop,所以按照bp sp bx cx dx 的顺保护,也会按照倒序恢复。保护sp的之后立马提升栈空间,恢复sp的时候也顺便就销毁栈空间了。最后函数结束内平栈(也可以外平栈)。

代码实例

assume  cs:code,ds:data,ss:stack  

;栈段(存放数据,比如高级语言中的局部变量)
stack segment 
    db 20 dup(0)
stack ends

;数据段(存放数据,比如高级语言中的全局变量)
data segment   
     db 20 dup(0)
     str db "Hello World!$"              
data ends

;代码段
code segment
start: 
    ;设置ds和ss
    mov ax,data
    mov ds,ax
    mov ax,stack
    mov ss,ax


    ;业务逻辑代码
    push 3h    ;传递参数
    push 4h
    call sum
    ;add sp,6   
    
    
      
    ;退出程序
    mov ah,4ch
    int 21h   
    ;参数:传递两个字型参数,参数分别用bx,dx存放
    ;返回值:返回值存放在ax中
 sum:
     ;保护bp
     push bp
     mov bp,sp    
     sub sp,20  ;20字节留作局部变量 
     ;保护寄存器
     push bx
     push cx
     push dx
     ;*****************业务逻辑代码
     ;定义两个局部变量
     mov ss:[bp - 2],1h
     mov ss:[bp - 4],2h  
     
     ;修改寄存器
     mov bx,2h
     mov cx,3h
     mov dx,4h  
     
     mov ax,ss:[bp + 2] 
     add ax,ss:[bp + 4] 
     add ax,ss:[bp - 2]
     add ax,ss:[bp - 4]  
     ;*****************业务逻辑代码 
     ;恢复寄存器
     pop dx
     pop cx
     pop bx
     
     ;恢复sp
     mov sp,bp   
     ;恢复bp
     pop bp
     
     ret 4  
      
code ends
end start


;int sum(int a, int b)
;{
;    int c = 1;
;    int d = 2;
;    return a + b + c + d;
;}
;函数的调用流程
;1.push参数(64位cpu 任性使用寄存器)
;2.call指令调用(将下一条指令地址入栈)
;3.保护bp寄存器,将sp赋值给bp
;4.提升sp指针,作为局部变量空间(sp 减去值)
;5.保护寄存器
;6.业务逻辑
;7.恢复寄存器
;8.恢复sp(sp指向bp/sp 加上值)
;9.恢复bp(pop bp)
;10.返回(ret)

看看xcode的代码
OC

int sum(int a, int b) {
    return a + b;
}

int main(int argc, char * argv[]) {
    
    int a =  sum(1, 2);
    
    printf("%d", a);
    return  0;
}

汇编 - main 函数

汇编`main:
    0x100000f30 <+0>:  pushq  %rbp ;保护bp
    0x100000f31 <+1>:  movq   %rsp, %rbp ;保护sp
    0x100000f34 <+4>:  subq   $0x20, %rsp ;提升栈空间
    0x100000f38 <+8>:  movl   $0x1, %eax  ;传参1
    0x100000f3d <+13>: movl   $0x2, %ecx  ;传参2
    0x100000f42 <+18>: movl   $0x0, -0x4(%rbp)  
    0x100000f49 <+25>: movl   %edi, -0x8(%rbp) ;保护di
    0x100000f4c <+28>: movq   %rsi, -0x10(%rbp) ;保护si
->  0x100000f50 <+32>: movl   %eax, %edi ;调用函数
    0x100000f52 <+34>: movl   %ecx, %esi
    0x100000f54 <+36>: callq  0x100000e90               ; sum at main.m:11
    0x100000f59 <+41>: leaq   0x3a(%rip), %rdi          ; "%d"
    0x100000f60 <+48>: movl   %eax, -0x14(%rbp)
    0x100000f63 <+51>: movl   -0x14(%rbp), %esi
    0x100000f66 <+54>: movb   $0x0, %al
    0x100000f68 <+56>: callq  0x100000f7a               ; symbol stub for: printf
    0x100000f6d <+61>: xorl   %ecx, %ecx
    0x100000f6f <+63>: movl   %eax, -0x18(%rbp)
    0x100000f72 <+66>: movl   %ecx, %eax
    0x100000f74 <+68>: addq   $0x20, %rsp ;恢复sp
    0x100000f78 <+72>: popq   %rbp  ;恢复bp
    0x100000f79 <+73>: retq     ;函数结束

汇编 - sum 函数

汇编`sum:
    0x100000e90 <+0>:  pushq  %rbp  ;保护bp
    0x100000e91 <+1>:  movq   %rsp, %rbp  ;保护sp
    0x100000e94 <+4>:  movl   %edi, -0x4(%rbp)  ;保护 di
    0x100000e97 <+7>:  movl   %esi, -0x8(%rbp)  ;保护 si
->  0x100000e9a <+10>: movl   -0x4(%rbp), %esi  ;取参
    0x100000e9d <+13>: addl   -0x8(%rbp), %esi  ;相加
    0x100000ea0 <+16>: movl   %esi, %eax  ;返回值
    0x100000ea2 <+18>: popq   %rbp  ;恢复bp
    0x100000ea3 <+19>: retq   ;函数结束

应为xcode是64位机,所以很多操作和8086有差异,例如:
传参使用的寄存器,而不是ss。
因为函数很简单,没有提升栈空间,而是直接用的红色区域(红灯区)计算。
因为保护的通用寄存器本身没有发生变化,所以就优化掉了恢复通用寄存器的操作。
但是中心思想是不变的。

tips

不同的cpu对于函数的处理实现是不一样的,所以只要把中心函数基本思想记好才能以不变应万变。光死记硬背是行不通的。

你可能感兴趣的:(汇编学习笔记-函数总结(8086))