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指令修改该全局变量的值。
在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指令修改该全局变量的值。