tiny6410裸机实验第1章--------------ARM汇编和编程基础(C与ARM汇编混合编程)

时间:2022-09-05 03:54:34

【说明】

             在嵌入式开发中,大部分情况下我们使用C语言来编程,但是在程序的最前面一小段硬件相关的操作,以及有时候对速度要求高的地方我们仍然需要使用汇编进行编程。这就使得我们必须学会如何衔接好C和汇编,具体的有:C语言调用汇编语言的变量或者函数汇编语言调用C语言的变量或者函数,以及在C语言中嵌入汇编语言。下面我们就分别进行介绍。

 

【汇编调用C语言函数】

               我们后面具体的编程中,首先遇到的是汇编中调用C语言,因为我们说过,程序的最前面是用汇编写的,那么总会有一个分界点,也就是从汇编转向C语言的点,这一点显然需要从汇编语言中调用C语言,怎么做呢?

               回忆上一节的内容,ATPCS规则告诉我们传递C函数参数的方法是先放寄存器还有参数就用栈来传,OK,我们的汇编就是这么调用C函数的。

 

               1】在调用C语言之前需要初始化栈指针SP,所谓初始化,也就是给SP赋一个值,一般是内存中的一个地址,这样,SP就指向这个地址,栈操作就基于这个地址进行,这个地址附近的内容就是栈内容,当然啦,你必须保证这个地址附近的内存空间是可用的,也就是没有什么重要的信息在里边,不然随着进栈出栈的进行,里面的内容也就被覆盖了。

 

               2】如果被调用的C函数没有参数,那就直接用“bl    C函数名”就可以了,没错就这么简单。。比如下面的指令

               文件a.S中

              ldr sp , =8*1024             //初始化栈指针

              bl   main                           //跳转到b.c的xxx函数执行

              文件b.c

              int xxx(void)

             {

                      return 0;

              }

              注意,也不是每次调用C函数都要初始化栈,第一次初始化就好了

 

             3】如果被调用的C函数有参数,根据ATPCS规则就应该这么玩

             文件a.S中

              ldr sp, =8*1024                 //初始化栈

              ldr r0, =9                             //传参数!  

              bl xxx                                  //跳转过去

              文件b.c

              void xxx(int start)

             {   

             }                 

 

             4】如果被调用的C函数有超过4个的参数,根据ATPCS规则就应该这么玩

              文件a.S

              mov  r0, #0                  /* start */

              mov  r1, #8                  /* end */

              mov  r2, #0                  /* a */

              mov  r3, #0                  /* b */

              ldr     sp, =6*1024 - 8

              mov  r4, #0

              str     r4, [sp]                /* c */

              mov  r4, #1

              str     r4,  [sp, #4]        /* d */

              bl    xxx

              文件b.c

              void xxx(int start, int end, int a, int b, int c, int d)

            {          

            }

 

【汇编调用C语言变量】

               在汇编中其实可以操作C语言中的变量的,但是实际上用的并不多

               在b.c中

               int     g_val = 5;

               在a.S中

               IMPORT g_val               //下面的指令中就可以用了

               ldr r0, =g_val                 //这么用

               是不是很简单!

 

【C语言中调用汇编函数】

               在C语言中调用汇编语言其实也是一眼就懂

               文件a.S中

               delay:

                       ldr  r0, =1024

              在b.c中

              void delay();                                 //首先要声明

              int xxx()

             {

                    delay();

             }                    //直接调用,没错不用实现 直接调用

 

【C语言中嵌入式汇编语言】

                GNU   C提供了一种在C语言中直接加入汇编语言的方法,

                1】最简单形式的内嵌汇编

                 int xxx()

                 {

                         asm(

                                  "mov r0, #0\n\t"

                                  "mov r1, r0\n\t"

                         );

                         return 0;

                   }

                  

                 2】内嵌汇编的模版应该是这样的,上边的只是简化版本

                  asm(code  :  output operand list  :  input operand list  :  clobber list);

                  下面是C语言的一个整型变量传递给汇编,逻辑左移一位后再传递给C语言的另外一个整型变量

                   asm("mov %[result], %[value], ror #1"  :  [result] "=r"(y)  : [value] "r" (x));

                   每一个asm语句被冒号分成了4个部分

                              1)第一个部分是汇编代码

                              2)第二个部分是输出列表,每一个条目由一对[]和被他包含的符号名组成,它后面跟着限制性字符串,再后面是圆括号括着的C变量。

                              3)第三个部分是输入列表,语法和输出一模一样

                              4)第四个部分叫破坏符列表,指明哪些部分会在汇编代码中被改变,本例中没有用到。

                              5)四个部分如果后面没有其他部分了,那么省略该部分可以连:都不用写,但是如果后面有,:不能省略,也就是如果你要省略该部分,你就保持它空 白,  但是分割的:还是要加上的。下面的例子就有input 没有 output

                                   asm("msr cpsr, %[ps]" : : [ps]"r"(status));

                             6)其实编译器有时候会把你写的内嵌汇编改得乱七八糟,以便和编译后的C有更好的结合,如果下面这样加入volatile 关键字就可以去掉优化

                                   asm volatile("mov r0, r0");

 

 

                 3】我们知道,C中变量是有大小的,比如int是4字节,char是1字节,那么要用汇编操作C中变量就要注意这些       

           unsigned char    LDRB/STRB

          unsigned short   LDRH/STRH

          unsigned int     LDR/STR

          char             LDRSB/STRSB

          short            LDRSH/STRSH

 

 

                4】 上边的例子中,"=r"中,"r"是指明了后面数的属性,有以下几种,

                              1)"i"    表示后面会跟一个立即数

                              2)"r"    表示后面跟一个变量

                              3)"m"  表示后面变量的地址

 

 

                5】上边的"=" ,是一个限制符,有以下几种,

                             1)"="    表示只可写,一般用在output 列表中

                             2)"+"    可读可写

                             3)"&"   表示输入输出用不同的寄存器

                 

 

                6】现在我们谈谈破坏列表,破坏列表指明了汇编代码中会改变哪些东西的值,让编译器注意。

                            1)"memory"   指明有些内存的内容被改变了,那么一些保存该内存内容的寄存器就应该被更新

                            2)"r0"   指明寄存器r0 被改变了,那么编译器就应该在内嵌汇编前边保存该寄存器的值,在内嵌汇编后边恢复它,当然r1, r2都行,r0只是举个例子