arm汇编编程 简单例子

ARM 107浏览

arm汇编编程 简单例子

1).基本概念       

    (2)寄存器如 R0、R1等  ARM的汇编编程本质上就是针对CPU寄存器的编程。   

     (3)指令即操作码直接控制CPU如MOV       包括跳转指令、数据处理指令、乘法指令、PSR访问指令、加载或存储指令、数据交换指令、移位指令等 

    (4)伪操作作用于编译器,大多用于定义和控制。如AREA       包括符号定义、数据定义、控制等

    (5)标号仅是一种标识。在跳转语句中可以指向要跳转到的标识号位置             在ARM 汇编中标号代表一个地址段内标号的地址在汇编时确定段外标号的地址值在连接时确定

    (6)符号:即标号(代表地址)、变量名、数字常量名等。符号的命名规则如下

         a. 符号由大小写字母、数字以及下划线组成

         b. 除局部标号以数字开头外其它的符号不能以数字开头

         c. 符号区分大小写且所有字符都是有意义的

         d. 符号在其作用域范围你必须是唯一的

         e. 符号不能与系统内部或系统预定义的符号同名

         f. 符号不要与指令助记符、伪指令同名。

   2).段定义 

     在汇编语言中以相对独立的指令或数据序列的程序段组成程序代码

      段的划分数据段、代码段。一个汇编程序至少有一个代码段

      (1)代码段

         AREA  定义一个段并说明所定义段的相关属性CODE 用以指明为代码段

         ENTRY 标识程序的入口点。END为程序结束。

      (2)数据段

         AREA   DATAAREADATA,BIINIT,ALLGN=2

         DISPBUF SPACE 200

         RCVBUF  SPACE 200

         DATA用以指明为数据段

     SPACE分配200字节的存储单元并初始化为0

   3).汇编语言结构

              [标号] [指令或伪操作]

             所有标号必须在一行的顶格书写其后不加冒号

             所有指令均不能顶格写

             指令助记符大小写敏感不能大小写混合只能全部大写或全部小写

             ;为注释

             @代码行注释同;

             #整行注释或直接操作数前缀 

            为换行符

             ENTRY 为程序的入口

             END  为程序的结束

 

ARM程序常见结构

  1.子函数和主函数

        使用BL 指令进行调用该指令会把返回的PC 值保存在LR

            AREA Example,CODE,READONLY ;声明代码段Example

            ENTRY                      ;程序入口

        Start 

            MOV  R0,#0                 ;设置实参,将传递给子程骗子的实参存放在r0和r1内

            MOV  R1,#10

            BL   ADD_SUM

               ;调用子程序ADD_SUM 

           B    OVER                  ;跳转到OVER标号处进入结尾

        ADD_SUM

            ADD  R0,R0,R1              ;实现两数相加

            MOV  PC,LR                 ;子程序返回R0内为返回的结果

        OVER

            END

 

 

 

1.宏定义(MACRO、MEND)‘

         格式:

            MACRO

                      {$标号名} 宏名{ $ 参数 1  $ 参数 2 „„}

                      指令序列

               MEND

 

           MACRO 、 MEND 伪指令可以将一段代码定义为一个整体称为宏指令在程序中通过宏指令多次调用该段代码。

           {}为可选项

           $ 标号在宏指令被展开时标号会被替换为用户定义的符号           在宏定义体的第一行应声明宏的原型(包含宏名、所需的参数)然后就可以在汇编程序中通过宏名来调用该指令序列

           写在代码段或数据段前面

           MEXIT 跳出宏

例:没有参数的宏实现子函数返回

         MACRO

                MOV_PC_LR    ;宏名

                MOV PC,LR    ;子程序返回R0内为返回的结果

             MEND

             AREA Example,CODE,READONLY ;声明代码段Example

             ENTRY                      ;程序入口

         Start

              MOV  R0,#0

                   MOV_PC_LR                  ;调用宏代表子函数结束

                   END

 

例:有参数宏         宏定义从MACRO 伪指令开始到MEND 结束并可以使用参数。类似于C的#define

         MACRO                ;宏定义

            CALL $Function,$dat1,$dat2 ;宏名称为CALL,带3 个参数

            IMPORT $Function  ;声明外部子程序 宏开始

            MOV R0,$dat1      ;设置子程序参数,R0=$dat1

            MOV R1,$dat2

            BL $Function      ;调用子程序 宏最后一句

         MEND                 ;宏定义结束

         CALL FADD1,#3,#2     ;宏调用后面是三个参数

         汇编预处理后宏调用将被展开程序清单如下

     IMPORT FADD1

         MOV R0,#3

         MOV R1,#3

         BL FADD

 

 

 

C和ARM汇编程序间的相互调用

 1. 汇编程序调用C子程序

    为保证程序调用时参数正确传递必须遵守ATPCS。

    在C程序中函数不能定义为static函数。在汇编程序中需要在汇编语言中使用IMPORT伪操作来声明C子函数

 2.汇编程序访问全局C变量

    汇编程序中可以通过C全局变量的地址来间接访问C语言中定义的全局变量

    在编编程序中用IMPORT引入C全局变量该C全局变量的名称在汇编程序中被认为是一个标号。通过ldr和str指令访问该编号所代表的地址

        //C代码

    int i=3;

    int sum5(int a, int b ,int c, int d)

    {

        return (a+b+c+d+i);

    }

       //汇编代码

             AREA Example,CODE,READONLY ;声明代码段Example

          IMPORT sum5

          IMPORT i

          ENTRY                      ;程序入口

    Start

           LDR  R1,i                  ;将i读入R1内

          MOV  R0,#2

          ADD  R0,R0,R1

          STR  R0,i                  ;将寄存器值写入i内

          MOV  R3,#4                 ;设置实参,将参数写入R0-R3

          MOV  R2,#3

          MOV  R1,#2 

          MOV  R0,#1

          BL   sum5                 ;调用子程序ADD_SUM

          B    OVER                  ;跳转到OVER标号处进入结尾

    OVER

          END

 

3. 在C语言中调用汇编子程序

    为保证程序调用时参数的正确传递在汇编程序中需要使用EXPORT伪操作来声明汇编子程序同时在C语言中使用extern扩展声明汇编子程序

4. 在C语言中调用汇编全局变量

    汇编中用DCD为全局变量分配空间并赋值并定义一个标号代表该存储位置。

    在汇编中用EXPORT导出标号这个标号就是全局变量在C程序中用extern扩展声明名该变量

        //汇编代码

          EXPORT func1

         EXPORT tmp

         AREA Example,CODE,READONLY ;声明代码段Example

    tmp              ;全局变量名

         DCD 0x0005  ;全局变量创建内存空间及赋初值

    func1            ;子函数名

         ADD  R0,R0,R1              ;实现两数相加

         ADD  R0,R0,R2 

         ADD  R0,R0,R3 

         MOV  PC,LR                 ;子程序返回R0内为返回的结果

         END

    //C代码

         extern int func1(int a,int b,int c,int d);

    extern int tmp;

    int main(int argc,char **argv)

{

        int a=1,b=2,c=3,d=4;

        int z=func1(a,b,c,tmp);

        printf("%d",z);

           return 0;

    }

 

5. 在C语言中内嵌汇编

    有些操作C语言程序无法实现如改变CPSP寄存器值初始化堆栈指针寄存器SP等这些只能由汇编来完成。

    但出于编程简洁等一些因素有时需要在C源代码中实现上述操作此时就需要在C中嵌入少量汇编代码。

    内嵌的汇编不能直接引用C的变量定义必须通过ATPCS进行语法格式如下:

 

__asm{

          //内嵌汇编

       }

例:

在C语言中嵌入汇编

    int f()

{

//C函数

       __asm{

 //内嵌汇编禁用中断例子

          MRS R0,CPSR

          ORR R0,R0,#0x80

          MSR CPSR_c,R0

       }

 }

    int main(int argc,char **argv)

{

       int a;

       int z=f(a);

       printf("%d",z);

       return 0;

}   

    出地完整性考虑内嵌汇编相对于一般汇编的不同特点如下

1)操作数可以是寄存器、常量或C表达式。可以是char、short、或int类型而且是无符号数进行操作

    2)常量前的#号可以省略

    3)只有指令B可以使用C程序中的标号,指令BL不可以使用

    4)不支持汇编语言中用于内存分配的伪操作

    5)内嵌汇编不支持通过“.”指示符或PC获取当前指令地址

    6)不支持LDR Rn,=expression 伪指令而使用MOV Rn,expression指令向寄存器赋值

    7)不支持标号表达式

    8)不支持ADR和ADRL伪指令

    9)不支持BX和BLX指令

    10)不可以向PC赋值 

    11)使用0x前缀替代 &表示十六进制数

    12)不使用寄存寻址变量

    13)ldm和stm指令的寄存器列表只允许物理寄存器

    14)必须小心使用物理寄存器如R0-R3,LR和PC