Chinaunix首页 | 论坛 | 博客
  • 博客访问: 9083
  • 博文数量: 5
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2017-06-25 15:52
文章分类
文章存档

2017年(5)

我的朋友
最近访客

分类: 嵌入式

2017-10-10 09:59:55

ARM汇编伪指令
    在ARM汇编语言程序里,有一些特殊指令助记符,这些助记符与指令系统的助记符不同,他们没有对应的操作码,
    也就是不会生成机器码,仅仅是在编译器软件中起着格式化的作用,通常称这些特殊指令助记符为伪指令。
    伪指令在源程序中的作用是为完成汇编程序作各种准备工作的,这些伪指令仅在汇编过程中起作用,
    一旦汇编结束,伪指令的使命就完成。
    ARM汇编程序中,有如下几种伪指令:数据常量定义伪指令,数据变量定义伪指令,内存分配伪指令,及其他伪指令。


数据常量定义伪指令
    数据常量定义伪指令EQU用于为程序中的常量,标号等定义一个等效的字符名称,类似于C语言中的#define
    EQU语法格式: 名称 EQU 表达式{,类型}
        名称为EUQ伪指令定义的字符名称,当表达式为32位的常量时,可以指定表达式的数据类型,可以有以下三种类型:CODE16,CODE32和DATA
数据变量定义伪指令
    数据变量定义伪指令用于定义ARM汇编程序中的变量,对变量赋值以及定义寄存器的别名等操作。常见的数据变量定义伪指令有如下几种:
    (1)GBLA,GBLL和GBLS
        语法格式: GBLA(GBLL或GBLS) 全局变量名
        GBLA,GBLL和GBLS伪指令用于定义全局变量,并将其初始化。其中
        GBLA用于定义一个全局的数据变量,并初始化为0
        GBLL用于定义一个全局的逻辑变量,并初始化为F(假)
        GBLS用于定义一个全局的字符创变量,并初始化为空
    (2)LCLA,LCLL,和LCLS
        语法格式:LCLA(LCLL或LCLS)局部变量名
        LCLA,LCLL,和LCLS伪指令用于定义一个ARM程序中的局部变量,并将其初始化,其中:
        LCLA伪指令用于定义一个局部的数据变量,并初始化为0
        LCLL伪指令用于定义一个局部的逻辑变量并初始化为F(假)
        LCLS伪指令用于定义一个局部的字符串变量,并初始化为空
    (3)SETA,SETL和SETS:用于给一个已经定义的全局变量或局部变量赋值
        语法格式: 变量名  STEA(SETL,SETS) 表达式
        SETA伪指令用于给一个数学变量赋值
        SETL伪指令用于给一个逻辑变量赋值
        SETS伪指令用于给一个字符串变量赋值
    (4)RLIST:用于对一个通用寄存器列表定义名称,使用该伪指令定义的名称可在ARM指令LDM/STM中使用。
        在LDM/STM指令中,列表中的寄存器访问次序为根据寄存器的编号由低到高,而与列表中的寄存器排列次序无关
        语法格式: 名称 RLIST {寄存器列表}


内存分配伪指令
    内存分配伪指令一般用于为特定的数据分配存储单元,同时可完成已分配存储单元的初始化。常见的数据定义伪指令有如下几种:
    (1)DCB:分配一段字节的内存单元,并用指定的数据初始化
        语法格式: 标号 DCB 表达式
    (2)DCW(或DCWU):分配一段半字的内存单元,并用指定的数据初始化
        语法格式: 标号 DCW(DCWU) 表达式
    (3)DCD(或DCDU):分配一段字的内存单元,并用指定的数据初始化(DCD伪操作为基于静态基地址寄存器R9的偏移量分配内存单元。)
        语法格式: 标号 DCD(DCDU) 表达式
    (4)DCFD(或DCFCU):分配一段双字的内存单元,并用双精度的浮点数据初始化
        语法格式: 标号 DCFD(DCFDU) 表达式
    (5)DCFS(或DCFSU):分配一段字的内存单元,并用单精度的浮点数据初始化。
        语法格式: 标号 DCFS(DCFSU) 表达式
    (6)DCQ(或DCQU):分配一段双字的内存单元,并用64位的整数数据初始化。
        语法格式: 标号 DCQ(DCQU) 表达式
    (7)SPACE:分配一块内存单元,并用0初始化。
        语法格式: 标号 表达式{,基址寄存器}
    (8)MAP:定义一个结构化的内存表的首地址
        语法格式: MAP 表达式{,基址寄存器}
    (9)FIELD:定义结构化的内存表中的一个数据域
        语法格式: 标号 FIELD 表达式

汇编控制伪指令
    控制伪指令用于控制汇编程序的执行流程,常用的汇编控制伪指令包括以下几条:
    (1) IF,ELSE,ENDIF 
        语法格式: IF 逻辑表达式
                                指令序列1 1
                           ELSE
                                指令序列2
                            ENDIF
    (2)WHILE,WEND(标识宏定义结束)
        语法格式: WHILE 逻辑表达式
                                指令序列
                            WEND
    (3)MEXIT
        语法格式: MEXIT
                MEXIT 用于从宏定义中跳转出去
    (4)MACRO,MEND(标识宏定义的结束)
        语法格式: MACRO  $label 宏名 $参数1 $参数2,...
                                指令序列
                            MEND
        $label在宏指令被展开时,label可被替换成相应的符号,通常是一个标号。在一个符号前使用$表示程序被汇编时将使用相应的值来替换$后的符号。
        MACRO伪操作标识宏定义的开始,MEND标识宏定义的结束。
        该指令可以将一段代码定义为一个整体,然后就可以在程序中通过宏指令多次调用该段代码。
其他常用的伪指令
    还有一些其他的伪指令,在汇编程序中经常会被使用,主要包括AREA,ALIGN,CODE16,CODE32,ENTRY,END,EXPOR(或GLOBAL),IMPORT,
    EXTERN,GET(或INCLUDE)INCBIN,RN,ROUT等。
    AREA:伪指令用于定义一个代码段或数据段。
    ALIGN:伪操作通过添加补丁字节使当前位置满足一定的对齐方式
    CODE16,CODE32:指示数据类型
    ENTRY:伪操作指定程序的入口点。
    END:伪操作告诉编译器已经到了源程序的结尾
    EXPORT:声明一个符号可以被其他文件引用,相当于声明了一个全局变量
    IMPORT:告诉编译器当前的符号不是在本源文件中定义的,而是在其他源文件中定义的,在本源文件中可能引用该符号
        而且不论本源文件是否实际引用该符号,该符号都将被加入到本源文件的符号列表中。
    EXTERN:告诉编译器当前的符号不是在本源文件中定义的,而是在其他源文件中定义的,在本源文件中可能引用该符号。如果
        本源文件没有实际引用该符号,该符号不会被加入到源文件的符号列表中。
    GET(INCLUDE):将一个源文件包含到当前源文件中,并将被包含的文件在其当前位置进行汇编处理。
    INCLBIN:将一个文件包含到当前源文件中,被包含的文件不进行汇编处理。
    RN:为一个特定的寄存器定义名称。
ARM的汇编语言结构
    在ARM汇编语言程序中,以相对独立的指令或数据序列的程序段为单位组织程序代码。
    段可以分为代码段和数据段。可执行映像文件通常由以下几部分构成:
        :一个或多个代码段,代码段的属性为只读
        :零个或多个包含初始化数据的数据段,数据段的属性为可读可写。
        :零个或多个不包含初始化数据的数据段,数据段的属性为可读可写。
Linux下GCC的汇编语言规则
    :所有标号必须在一行的顶格书写,并且其后必须添加":"号
    :所有的指令均不能顶格写。
    :大小写敏感。
    :注释使用分号”@“


ARM汇编语言与C语言混合编程
    ARM体系结构支持c/c++以及汇编语言的混合编程,在一个完整的程序设计中,除了初始化部分用汇编完成外,其主要的编程任务一般都用C/C++完成。
    汇编语言和C/C++的混合编程通常有以下几种方式:
        :汇编程序中调用c程序
        :C程序中调用汇编程序
        :c程序中内嵌汇编语句
        :从汇编程序中访问C程序变量
基本的ATPCS
    基本的APTCS规定了在混合编程时,子程序调用的一些基本规则,主要包括寄存器的使用,堆栈的使用,参数传递和子程序结果的返回等方面的规则、
    寄存器的使用规则
        :程序通过R0-R3来传递参数,这时这些寄存器可以记作A0-A3,被调用的子程序在返回前无需回复寄存器R0-R3的内容
        :在子程序中,使用R4-R11来保存局部变量,这时这些寄存器可以记做V1-V8
        :寄存器R12用作子程序间scratch寄存器,记做IP,在子程序的连接代码段中经常会有这种使用规则
        :寄存器R13用作数据栈指针,记做SP,在子程序中寄存器R13不能用做其他用途。
        :寄存器R14用作连接寄存器,记作LR,它用于保存子程序的返回地址
        :寄存器R15是程序计数器,记作PC,它不能用作其他用途。
ATPCS中的各寄存器在ARM编译器和汇编器中都是预定义的
    堆栈的使用规则:
        :栈指针通常可以指向不同的位置,当栈指针指向栈顶元素时,
            称为FULL栈。当栈指针指向与栈顶元素相邻的一个元素时,称为Empty栈。
            数据栈的增长方向也可以不同,当数据栈向内存减小的地址方向增长时称为Descending栈,反之称为Ascending栈。
    ATPCS规定数据栈为FD类型,并且对数据栈的操作是8字节对齐的
    参数的传递规则:
        根据参数个数是否固定,可以将子程序分为参数个数固定的子程序和参数个数可变的子程序,
        这两种子程序的参数传递规则不同。
        参数个数可变的子程序参数传递规则:
            当参数个数不超过4个的时候,可以使用R0-R3来传递参数,当超过4个的时候还可以使用数据栈来传递参数。
        参数个数固定的子程序参数:
            第一个整数参数,通过寄存器R0-R3来传递,其他的参数通过数据栈来传递。
            浮点参数也有特别的传递规则。
    子程序结果返回规则:
        结果为一个32位的整数时,可以通过寄存器R0返回
        结果为一个64位的整数时,可以通过R0和R1返回,以此类推。
        结果为一个浮点数时,可以通过浮点运算部件的寄存器f0,d0或s0来返回。
        对于位数更多的结果,则需要通过调用内存来传递。
汇编和C程序的互相调用
    汇编程序中调用C程序:
        汇编程序的设计要遵守ATPCS规则,保证程序调用时参数的正确传递。
        在汇编程序中使用IMPORT伪操作声明将要调用的C程序。汇编中可通过BL调用C函数
    C程序中调用汇编程序:
        在汇编程序中使用EXPORT伪指令声明程序,使得本程序可以被其他的程序调用。
        在C语言中使用EXTERN关键字声明该汇编程序,这样就可以在C中使用该函数了。
    C程序中内嵌汇编语句:
        在C中内嵌的汇编指令支持大部分的ARM和Thumb指令,不过在其使用与汇编文件中的指令有些不同
        1:不能直接向PC寄存器赋值,程序跳转要使用B或者BL指令
        2:在使用物理寄存器时,不要使用过于复杂的C表达式,避免物理寄存器冲突。
        3:R12和R13可能被编译器用来存放中间编译结果
        4:一般不要直接指定物理寄存器,而让编译器进行分配。
    从汇编程序中访问C程序变量
        在C程序中声明的全局变量可以被汇编程序通过地址间接访问,具体访问方法如下:
        1:使用IMPORT伪指令声明该全局变量
        2:使用LDR指令读取该全局变量的内存地址,通常该全局变量的内存地址值存放在程序的数据缓冲区中
        3:根据该数据的类型,使用相应的LDR指令读取该全局变量的值,使用相应的STR指令修改该全局变量的值。
阅读(1108) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~