C语言与汇编语言之间的函数调用

教材:嵌入式系统及应用,罗蕾、李允、陈丽蓉等,电子工业出版社

  • ARM 程序设计
    • C与汇编之间的函数调用
      • ATPCS简介
        • 堆栈与寄存器在函数调用中的作用
        • ATPCS关于堆栈和寄存器的使用规则
      • C程序调用汇编函数实例
      • 汇编程序调用C函数实例
    • CC语言和汇编语言的混合程序设计
      • 内联汇编
      • 嵌入式汇编
      • 内联汇编与嵌入式汇编的差异


ARM 程序设计

C与汇编之间的函数调用

ATPCS简介

  • ARM-Thumb 过程调用标准 ATPCS(ARM-Thumb Procedure Call Standard)
  • ATPCS 标准既是ARM 编译器使用的函数调用规则,也是设计可被 C 程序调用的汇编函数的编写规则

堆栈与寄存器在函数调用中的作用

  • 函数是通过寄存器和堆栈来传递参数和返回函数值的
  • 形参和返回值都应定义在具有暂存性质的寄存器和堆栈中

ATPCS关于堆栈和寄存器的使用规则

  • ATPCS 规定,ARM 的数据堆栈为 FD 型堆栈,即满递减堆栈
    C语言与汇编语言之间的函数调用_第1张图片

  • 对于参数个数不多于 4 的函数,编译器必须按参数在列表中的顺序,自左向右 为它们分配寄存器 R0~R3

  • 其中函数返回时,R0 还被用来存放函数的返回值

  • 如果函数的参数多于 4 个,那么多余的参数则按自右向左的顺序压入数据堆栈(参数入栈顺序与参数顺序相反)

  • 一个浮点数可能几个整数型的寄存器进行传送

  • 双精度和long long类型的参数通过两个连续的寄存器来传递,返回值通过R0和R1返回(大端系统下,R0包含高位有效字)

  • ATPCS规定的寄存器名称及使用
    C语言与汇编语言之间的函数调用_第2张图片

    RWPI:读写位置无关(编译器选项)

  • 为确保性能,函数内部最多使用12个局部变量

  • 寄存器的别名和特殊名称都是ARM编译器和汇编器预定义的,用户可以直接使用

C程序调用汇编函数实例

  • 程序员代替C编译器把C函数翻译成汇编程序

  • 下面是一个用汇编语言编写的函数,该函数把 R1 指向的数据块复制到 R0 指向的存储块

    AREA Strcopy, CODE, READONLY
           EXPORT strcopy       ;声明strcopy为导出符号
    strcopy  
           LDRB R2, [R1], #1    ;R1的值为源数据首地址
           STRB R2, [R0], #1    ;R0的值为目标数据块首地址
           CMP R2, #0
           BNE strcopy
           MOV PC, LR       ;复制完毕,返回
           END
  • 根据参数与寄存器直接的规则,可知汇编函数 strcopy 在C程序中原型应该为:void strcopy(char *d, const char* s);

  • 在C语言文件中,调用 strcopy 函数的方法如下

    extern void strcopy(char *d, const char * s); //声明strcopy为外部引用符号
    int main(void)
    {
      const char *src = “source”;
      char dest[10];
    ...
    strcopy(dest, src);     //调用汇编函数strcopy
    ...
    }

汇编程序调用C函数实例

  • 现有C函数 g() 如下

    int g(int a, int b, int c, int d, int e)
    {
    return a+b+c+d+e;
    }
  • 汇编函数f中调用C函数g(),以实现下面的功能

    int f(int i) 
    {
    return –g(i, 2*i, 3*i, 4*i,5*i);
    }
  • 整个汇编函数 f 的代码如下

    EXPORT    f   
    AREA   f,  CODE,  READONLY
    IMPORT   g   ;声名g为外部引用符号
    STR   LR,   [SP, #-4]  ;断点存入堆栈
    ADD  R1, R0, R0  ;(R1)= i*2
    ADD  R2, R1, R0  ;(R2)= i*3
    ADD  R3, R1, R2  ;(R3)= i*5
    STR   R3, [SP, #-4]   ;将(R3)即第5个参数i*5存入堆栈
    ADD  R3, R1, R1 ;(R3)= i*4
    BL  g  ;调用C函数g(),返回值在寄存器R0中
    ADD SP, SP, #4    ;清栈
    RSB R0, R0, #0    ;函数f的返回值(R0)=0-(R0)
    LDR PC, [SP], #4  ;恢复断点并返回
    END

    调用时第5个参数放在数据堆栈段,所以调用C函数后需要清栈

C/C++语言和汇编语言的混合程序设计

在 C 程序中内联或嵌入式汇编代码,以提高程序的效率

内联汇编

  • 在 C 程序中直接编写汇编程序段而形成一个语句块,这个语句块可以使用除了 BX 和 BLX之外的全部ARM指令来编写

  • 可以使程序实现一些不能从C获得的底层功能

  • void enable_IRQ(void)
    {
    int tmp;
      _ _asm     //声名内联汇编代码
      {
          MRS  tmp, CPSR
          BIC  tmp, tmp, #0x80
          MSR  CPSR_c, tmp
      }
    }
  • 汇编语句块中,如果有两条指令占据了同一行,那么必须用分号“ ;”将它们分隔

  • 如果一条指令需要占用多行,那么必须用反斜线符号“ \ ”作为续行符

  • 可以在内联汇编语言块内的任意位置使用C/C++格式的注释

  • 内联汇编代码中定义的标号可被用作跳转或C/C++ goto 语句的目标,同样,在C/C++代码中定义的标号,也可被用作内联汇编代码跳转指令的目标

  • 内联汇编的限制

    • 它不支持 Thumb 指令;除了程序状态寄存器 PSR 之外,不能直接访问其他任何物理寄存器
    • 如果在内联汇编程序指令中出现了以某个寄存器名称命名的操作数,那么它被叫做虚拟寄存器,而不是实际的物理寄存器。编译器在生成和优化代码的过程中,会给每个虚拟寄存器分配实际的物理寄存器,但这个物理寄存器可能与在指令中指定的不同。
    • 在内联汇编代码中不能使用寄存器 PC(R15)、LR(R14)和SP(R13)
    • 更改处理器模式会禁止使用 C 操作数或对已编译 C 代码的调用,直到将处理器模式恢复为原设置之后
  • 状态寄存器 PSR ,任何对 PSR 的引用总是执行指向物理 PSR

  • 在内联汇编语句块中最好使用 C 或 C++ 变量作为操作数

嵌入式汇编

  • 嵌入式汇编程序是一个编写在C程序外的单独汇编程序,该程序段可以像函数那样被 C 程序调用

  • 嵌入式汇编具有真实汇编的所有特性,数据交换符合 ATPCS 标准,同时支持 ARM 和Thumb,所以它可以对目标处理器进行不受限制的低级访问

  • 不能直接引用 C/C++ 的变量

  • 定义一个嵌入式汇编函数的语法格式为

    _ _asm returntype functionname(parameter-list)
    {
      汇编程序段
    }
    • return–type:函数返回值类型,C语言中的数据类型
    • function–name:函数名
    • parameter-list:函数参数列表
  • 参数名只允许使用在参数列表中,不能用在嵌入式汇编函数体内

  • 在 C 程序中调用嵌入式汇编程序的方法与调用 C 函数的方法相同

内联汇编与嵌入式汇编的差异

  • 内联汇编代码使用高级处理器抽象,并在代码生成过程中与 C 和 C++代码集成。因此编译程序将 C 和 C++代码与汇编代码一起进行优化

  • 嵌入式汇编代码从 C 和 C++ 代码中分离出来单独进行汇编,产生与 C 和 C++ 源代码编译对象相结合的编译对象

  • 可通过编译程序来内联汇编代码,但无论是显示还是隐式,都无法内联嵌入式汇编代码

  • C语言与汇编语言之间的函数调用_第3张图片

你可能感兴趣的:(嵌入式系统及应用,arm,汇编语言,c语言)