美文网首页程序员汇编
汇编学习笔记-函数总结(8086)

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

作者: sqatm | 来源:发表于2018-04-17 23:49 被阅读111次

    汇编函数结构

    • 函数外传递函数参数(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)

        本文链接:https://www.haomeiwen.com/subject/cqlmkftx.html