简单ARM指令集介绍

1.ARM指令集简介

   ARM指令集是基于精简指令集计算机(RISC)设计的,其指令集的译码机制相对比较简单,ARMv7-A具有32bit的ARM指令集和16/32bit的Thumb/Thumb-2指令集,ARM指令集的优点是执行效率高但不足之处也很明显,就是代码密度相对低一些。而作为ARM指令集子集的Thumb指令集,代码密度相对比ARM指令高,而且坚持了ARM一贯的性能优但也有一个致命的缺点就是效率低;正所谓鱼和熊掌不可兼得,这也是数字逻辑电路设计所谓的时间和空间的问题;而Thumb-2指令集多为32bit的指令,对于上述的ARM指令和Thumb指令做了一个折中,代码执行效率和密度都相对比较适中。几乎所有的ARM指令都可以条件执行,而另外两者仅有部分才具备此功能,三种指令均可相互调用,而且指令之间状态切换开销很小,几乎可以忽略。

1.1 ARM指令集格式

基本格式: {} {S} , , {}
< > 尖括号里面的指令助记符是必须的,而{}花括号里面的是可选的。
.opcode:比如MOV,LDR

.cond:即Condition,执行条件,与CPSR的条件标志位对应。

简单ARM指令集介绍_第1张图片

图1 条件码

.S:决定是否影响CPSR的值,见图1
.Rd:目标寄存器
.Rn:第一个操作数的寄存器
.opcode2:第二个操作数,可选,可以是立即数、寄存器、寄存器移位等

1.2 ARM汇编指令分类

包括存储加载类指令集,数据处理类指令集,分支跳转类指令集,程序状态寄存器访问指令以及协处理器类指令集

1>.存储加载类:

由于ARM处理器采用了统一编址技术,因而对外围I/O,程序数据的访问都要通过加载/存储(Load/Store)指令来进行。ARM的加载/存储指令(LDR,STR)是可以实现字,半字,无符号,有符号字节操作;

批量加载/存储(LDM,STM)可以实现一条指令加载存储多个存储器的内容,加载效率大为提高,一般用来传递参数和复制数据,可以说是一般加载/存储的加强版。
LDR:用于从内存中读取数据加载到内存中;比如 LDR R0, [R1]表示将R1所指向的存储单元的内容加到R0寄存器中
STR:将寄存器中的数据保存到内存单元;STR R0, [R1]将R0寄存器里面的数据保存到R1所指向的内存中。
LDM:实现一块连续的内存单元的数据加载多个寄存器中
STM:实现在多个寄存器的数据保存到一块连续的内存单元之中。
格式:LDM/STM  {cond} Rn{!} {reglist} {^}
.cond:同上

.mode:地址变化模式共8种,常用IA,FD分别表示每次传送数据后地址加4,滿递减堆栈。

简单ARM指令集介绍_第2张图片

图2  地址变换模式

.Rn:基址寄存器,不允许是R15.
.!:感叹号表示是否将最后的地址存入Rn
.Reglist:寄存器列表,按从小到大的顺序排列,当标号连续时可用'-'连接,{R0-R3},不连续时用逗号连接
."^":(假如寄存器列表含有PC寄存器R15)表示指令执行后SPSR的值自动复制给CPSR,常用于从中断处理函数中返回。
反之,默认操作的是用户模式下的寄存器,并非当前特殊模式的寄存器。
试问LDMIA R0, {R1-R4}和LDMIA R0!, {R1-R4}有什么区别?附:在ARM状态下执行32位字对齐的ARM指令;
区别就在于后者将最后的地址赋给了R0!!!!

2>.数据处理类指令集

包括数据传送指令MOV,算术逻辑运算符ADD,SUB,BIC,ORR,比较指令CMP,TST等

.MOV:寄存器与寄存器之间的数据传送,或者立即数传到目标寄存器中。
MOV R0, #8
MOV R0, R1
MOV R0, R1, LSL #3
MOV PC, LR @该指令可以实现子程序的返回,PC和LR由ARM汇编器对ARM寄存器做了预定义。PC即为R15,LR即为R14,也就等于:MOV R15, R14
.ADD,SUB:分别是加减指令
ADD R0, R1, #1
ADDS R0, R1, #1@指令执行后可能会影响CPSR的条件标志位。
BIC R0, R2, #0xF@将R2的后4位置清零。
ORR R0, R0, #oxF@将R0的后4位置1(与“1”做或运算,实现置1功能),结果保存到R0
CMP:指令格式 CMP {cond} Rn opcode2,将Rn的值减opcode2,根据操作结果更新相应CPSR的标志位。以便后面的指令判断是否执行
TST:位测试指令格式:TST {cond} Rn, opcode2将Rn的值与opcode2进行按位与操作,根据结果更新CPSR标志位。
SWP:交换指令: SWP {cond} {B} Rd, Rm, [Rn]:将Rn指向的内容加载到目标寄存器Rd,Rm为源寄存器,将该寄存器的数据存储到Rn指向的地址单元。

3>.分支跳转指令:B,BL

.B:直接跳转到指定地址执行,跳转范围+-32MB,格式:B {codn} label
.BL:带返回地址的跳转,指令自动将下一条指令的地址复制到连接寄存器R14中,然后跳转到指定的地址去执行,执行完后返回到跳转前指令的下一条指令处执行。

4>.程序状态寄存器访问指令

通过MSR和MRS配合使用实现对PSR寄存器的访问,通过读-修改-写操作来实现开关中断,切换处理器模式。
.MRS:读程序状态寄存器指令,将PSR中的内容读入到寄存器中MRS {cond} Rd, PSR
.MSR:写程序状态寄存器指令 MSR {cond} psr_fields #immed_8MSR {cond} psr_fields, Rm。field指位域。只有在特权模式下才能对PSR进行修改,
例如切换到管理模式:MSR CPSR_c #0xD3,将0xD3写入CPSR的低8位,此时M[4:0]=0b10011,进入管理模式。
用读-修改-写操作切换到管理模式
MRS R0, CPSR @读出CPSR的值
BIC R0, R0, #0x1F@清0
ORR R0, R0, #0xD3@修改模式
MSR CPSR_cxsf, R0@将修改后的值保存到CPSR

5>.协处理器访问指令

协处理器CP15包含了16个32bit的寄存器,主要用于存储管理
.MCR:ARM寄存器到协处理器的数据传送指令 MCR {cond} P15, 0, Rd, CRn, CRm, {opcode2}
Rd:源寄存器
CRn:协处理器中的寄存器,目标寄存器,存放第一个操作数其编号为C0,C1....C15
.MRC:协处理器到ARM寄存器的数据传送指令
Rd:目标寄存器
CRn:协处理器中的寄存器,源寄存器,存放第一个操作数其编号为C0,C1....C15
CRm:附加的源寄存器,不需要其他信息时CRm为C0.
opcode2:提供附加信息,若为空时,指定为0即可

2.ARM寻址方式

1>.立即寻址:MOV R1, #3
2>.寄存器寻址:MOV R0, R1
3>.寄存器移位寻址:MOV R1, R0, LSL #3
4>.寄存器间接寻址:LDR R0, [R2];将R2指向地址开始的一个字的内容加载到R0.
5>.基址寻址:LDR R1, [R0, #4]
6>.多寄存寻址:一次传送几个寄存器的值。上面已经写了,这边不再赘述了。
7>.堆栈寻址:STMFD SP!, {R0-R2}以滿递减堆栈进行压栈,LDMFD SP!, {R0-R2},数据出栈,放入R0-R2;(默认是小端操作格式)感叹号表示最后堆栈指令更新。
详细的寻址方式可参考七种寻址方式(立即寻址、寄存器寻址)这一篇文章。

3.ARM伪指令和伪操作

伪指令是汇编程序对源程序汇编期间由汇编程序将其替换成合适的ARM指令或者Thumb指令,最终转换成机器指令,常用的有小范围常数或地址加载伪指令ADR,中范围常数或地址加载伪指令ADRL,大范围常数或地址加载伪指令LDR;而伪操作包括符号定义伪操作、数据定义伪操作,汇编控制伪操作,信息报告伪操作等,所有的伪操作均以.开始。
1>.伪指令
ADR:ADR指令被编译器用一条ADD或者SUB进行替换,在ARM状态下,字对齐时加载范围是-1020~1020,字节或者半字对齐时是-255~255。
ADRL:被编译器用两条条ADD或者SUB进行替换,在ARM状态下,字对齐时加载范围是-256K~256K,字节或者半字对齐时是-64K~264K。
LDR:实现将一个32位常数或地址加载到寄存器。
例:通过LDR伪指令,完成GPIO的配置功能,将0xE0200280赋给R1
LDR R1, =0xE0200280
LDR R0, =0x00001111
STR R0, [R1]
2>.GNU ARM伪操作
.align n 伪操作指示编译器将代码段或者数据段以某种方式对齐。n表示以2的n次方字节对齐。
.byte、 .hword、 .word expression分别是在目标文件中插入一个字节,半字,字的值,若需插入多个值,以','隔开。
.data 告诉编译器下面的语句将被编译到可执行文件的数据段。
.text 告诉编译器下面的语句将被编译到可执行文件的代码段。
.extern .extern告诉编译器当前符号不是在本源文件中定义,在其他源文件中定义,在本源文件中可能用到。
.global symbol 表示声明外部标号,即当前标号是在本源文件中定义,在其他文件中可能被引用。
.end 告诉编译器该文件的语句的结束,汇编程序不需要处理之后的任何内容了。
.equ symbol, expression定义常量,类似于C语言的#define宏定义
.include "filename"文件包含伪操作,指示编译器将文件内容插到当前位置开始的地方。
.marcro 宏定义标志,以.marcro表示开始,以.endm表示定义结束,.exitm表示跳出宏定义。

4.APCS规则

APCS(ARM Process Call Standard)也就是指过程调用规则,定义了一系列规则来保证ARM汇编语言和C程序之间能够协调工作。涉及到的函数参数传递问题,返回值传递以及和函数调用过程中的
寄存器的使用,堆栈的使用等问题。

1>.寄存器使用

     APCS中,R0-R3用来传递参数,传递参数给子程序和返回子程序结果;R4-R11保存函数的局部变量(Thumb指令集只能使用R4-R7),R12(IP)也能被用在子程序间传递立即数(ARM状态下);

R13(SP)用来做堆栈指针,保存当前处理器模式的栈顶指针,链接寄存器R14(LR)保存子程序的返回过程。

2>.参数传递规则

当参数个数不超过4个时,可用上述的4个寄存器来传递,否则超过的参数使用栈来传递,对于子程序的返回结果,可用R0-R3来传递。

3>.函数的返回值

若返回值是32位的整数时,一般通过寄存器R0来传递,如果是64位的整数时,用R0和R1来传递。

5.arm-linux-gcc编译器

1>.预处理:将预处理输入文件后缀名为".c",".S",输出为".i"工具:arm-linux-cpp
arm-linux-gcc -E -o *.i  *.c/*.S
2>.编译:完成源代码从高级语言到特定的汇编语言代码的转换工具:ccl
arm-linux-gcc -S -o *.s *c
3>.汇编:将编译得到的".s"文件按照一定的指令集转换成一定格式的机器码工具:arm-linux-as
arm-linux-gcc -c -o *.o  *.c/*s/*.S
4>.链接:将汇编生成的目标文件和系统库的目标文件,库文件组装起来,生成在特定处理器平台运行的可执行文件。工具:arm-linux-ld
arm-linux-gcc -o *.c/*s/*S
除了上面的-E, -S, -c, -o选项外,还有-v,-g,-Wall,-Ox,(x=1,2,3...)等选项


你可能感兴趣的:(嵌入式)