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 DATAAREADATA,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