ARM_汇编
来源:互联网 发布:淘宝零食店排行 编辑:程序博客网 时间:2024/05/22 14:26
ARM 微处理器可支持多达 16 个协处理器,用于各种协处理操作,在程序执行的过程中,每个协处理器只执行针对自身的协处理指令,忽略 ARM 处理器和其他协处理器的指令。ARM 的协处理器指令主要用于 ARM 处理器初始化 ARM 协处理器的数据处理操作,以及在ARM 处理器的寄存器和协处理器的寄存器之间传送数据,和在 ARM 协处理器的寄存器和存储器之间传送数据。 ARM 协处理器指令包括以下 5 条:
— CDP 协处理器数操作指令
— LDC 协处理器数据加载指令
— STC 协处理器数据存储指令
— MCR ARM 处理器寄存器到协处理器寄存器的数据传送指令
— MRC 协处理器寄存器到ARM 处理器寄存器的数据传送指令
1、CDP 指令
CDP 指令的格式为:
CDP{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理 器操作码2。 CDP 指令用于ARM 处理器通知ARM 协处理器执行特定的操作,若协处理器不能成功完成特定的操作,则产生未定义指令异常。其中协处理器操作码1 和协处理器操作码2 为协处理器将要执行的操作,目的寄存器和源寄存器均为协处理器的寄存器,指令不涉及ARM 处理器的寄存器和存储器。
指令示例:
CDP P3 , 2 , C12 , C10 , C3 , 4 ;该指令完成协处理器 P3 的初始化
2、LDC 指令
LDC 指令的格式为:
LDC{条件}{L} 协处理器编码,目的寄存器,[源寄存器]
LDC 指令用于将源寄存器所指向的存储器中的字数据传送到目的寄存器中,若协处理器不能成功完成传送操作,则产生未定义指令异常。其中,{L}选项表示指令为长读取操作,如用于双精度数据的传输。
指令示例:
LDC P3 , C4 , [R0] ;将 ARM 处理器的寄存器 R0 所指向的存储器中的字数据传送到协处理器 P3 的寄存器 C4 中。
3、STC 指令
STC 指令的格式为:
STC{条件}{L} 协处理器编码,源寄存器,[目的寄存器]
STC 指令用于将源寄存器中的字数据传送到目的寄存器所指向的存储器中,若协处理器不能成功完成传送操作,则产生未定义指令异常。其中,{L}选项表示指令为长读取操作,如用于双精度数据的传输。
指令示例:
STC P3 , C4 , [R0] ;将协处理器 P3 的寄存器 C4 中的字数据传送到 ARM 处理器的寄存器R0 所指向的存储器中。
4、MCR 指令
MCR 指令的格式为:
MCR{条件} 协处理器编码,协处理器操作码1,源寄存器,目的寄存器1,目的寄存器2,协处理器操作码2。
MCR 指令用于将ARM 处理器寄存器中的数据传送到协处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。其中协处理器操作码1 和协处理器操作码2 为协处理器将要执行的操作,源寄存器为ARM 处理器的寄存器,目的寄存器1 和目的寄存器2 均为协处理器的寄存器。
指令示例:
MCR P3 , 3 , R0 , C4 , C5 , 6 ;该指令将 ARM 处理器寄存器 R0 中的数据传送到协处理器 P3 的寄存器 C4 和 C5 中。
5、MRC 指令
MRC 指令的格式为:
MRC{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2。
MRC 指令用于将协处理器寄存器中的数据传送到ARM 处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。其中协处理器操作码1 和协处理器操作码2 为协处理器将要执行的操作,目的寄存器为ARM 处理器的寄存器,源寄存器1 和源寄存器2 均为协处理器的寄存器。
指令示例:
MRC P3 , 3 , R0 , C4 , C5 , 6 ;该指令将协处理器 P3 的寄存器中的数据传送到 ARM 处理器寄存器中.
The ARM920T 有两个具体协处理器
CP14(略)
CP15 —系统控制协处理器 (the system control coprocessor)他通过协处理器指令MCR和MRC提供具体的寄存器来配置和控制caches、MMU、保护系统、配置时钟模式(在 bootloader时钟初始化用到)……
CP15的寄存器只能被MRC和MCR(Move to Coprocessor from ARM Register )指令访问
他包括15个具体的寄存器如下:
-R0:ID号寄存器 这是一个只读寄存器,返回一个32位的设备ID号,32具体功能参考2410 datesheet的 table 2-5
-R0:缓存类型寄存器(CACHE TYPE REGISTER) ,注意有2个R0,根据MCR操作数的不同传送不同的值,这也一个只读寄存器,包含了caches的信息。读这个寄存器的方式是通过设置协处理操作码为 1.
如:
MRC p15,0,Rd,c0,c0,1; 返回caches的详细信息
详见:table 2-6
-R1:控制寄存器 2-10
-R2:转换表基址寄存器(Translation Table Base --TTB) 2-12
-R3:域访问控制寄存器(Domain access control )2-13
-R4:保留 2-14
-R5:异常状态寄存器(fault status -FSR)2-14
-R6:异常地址寄存器(fault address -FAR)2-15
-R7:缓存操作寄存器 2-15
-R8:TLB操作寄存器 2-18
-R9:缓存锁定寄存器 2-19
-R10:TLB 锁定寄存器 2-21
-R11-12&14:保留
-R13:处理器ID 2-22
-R15:测试配置寄存器 2-24
MMU提供的一个关键服务是,能使各个任务作为独立的程序在其自己的私有存储空间运行.地址转换过程允许运行的多个程序使用相同的虚拟地址,而各自存储在物理存储器的不同位置.
(1)区域可以相互重叠;
(2)每个区域都分配有一个优先级,该优先级与分配区域的权限无关;
(3)当区域重叠时,具有最高优先级的区域的属性可以覆盖其他区域的属性,优先级仅作用于重叠部分的地址;
(4)区域的起始地址必须是其大小的倍数;
(5)区域的大小可以是4KB~4GB之间的任何2的乘幂;
(6)访问所定义区域外的存储器将产生异常.如果是内核预取指令,则MPU产生预取指令中止异常;如果是存储器数据请求,则产生数据中止异常.
在启用存储器保护单元之前,必须至少定义一个数据区域和一个指令区域,而且必须在启用cache和写缓冲器之前(或同时)启用存储器保护单元.控制器通过设置CP15的主寄存器c1~5来配置MPU.通过配置寄存器c2和c3来设置区域的cache和写缓冲器的属性,寄存器c5控制区域的访问权限,在寄存器c6里有8个或16个次寄存器用来定义每个区域的大小和位置.初始化MPU,cache和写缓冲器需要以下步骤:
(1)使用CP15:c6来定义指令和数据区域的大小和位置
(2)使用CP15:c5来设置每个区域的访问权限
(3)分别使用CP15:c2和CP15:c3来设置每个区域的cache和写缓冲器属性
(4)使用CP15:c1来使能cache和MPU
一般的中断控制器里我们可以配置与控制器相连的某个中断输入是FIQ还是IRQ,所以一个中断是可以指定为FIQ或者IRQ的,为了合理,要求系统更快响应,自身处理所耗时间也很短的中断设置为FIQ,否则就设置了IRQ。
如果该中断设置为了IRQ,那么当该中断产生的时候,中断处理器通过IRQ请求线告诉ARM,ARM就知道有个IRQ中断来了,然后ARM切换到IRQ模式运行。类似的如果该中断设置为FIQ,那么当该中断产生的时候,中断处理器通过FIQ请求线告诉ARM,ARM就知道有个FIQ中断来了,然后切换到FIQ模式运行。
简单的对比的话就是FIQ比IRQ快,为什么快呢?
ARM的FIQ模式提供了更多的banked寄存器,r8到r14还有SPSR,而IRQ模式就没有那么多,R8,R9,R10,R11,R12对应的banked的寄存器就没有,这就意味着在ARM的IRQ模式下,中断处理程序自己要保存R8到R12这几个寄存器,然后退出中断处理时程序要恢复这几个寄存器,而FIQ模式由于这几个寄存器都有banked寄存器,模式切换时CPU自动保存这些值到banked寄存器,退出FIQ模式时自动恢复,所以这个过程FIQ比IRQ快.
FIQ比IRQ有更高优先级,如果FIQ和IRQ同时产生,那么FIQ先处理。
在symbian系统里,当CPU处于FIQ模式处理FIQ中断的过程中,预取指令异常,未定义指令异常,软件中断全被禁止,所有的中断被屏蔽。所以FIQ就会很快执行,不会被其他异常或者中断打断,所以它又比IRQ快了。而IRQ不一样,当ARM处理IRQ模式处理IRQ中断时,如果来了一个FIQ中断请求,那正在执行的IRQ中断处理程序会被抢断,ARM切换到FIQ模式去执行这个FIQ,所以FIQ比IRQ快多了。
另外FIQ的入口地址是0x1c,IRQ的入口地址是0x18。
汇编存储cachec扩展编译器在嵌入式开发中,汇编程序常常用于非常关键的地方,比如系统启动时初始化,进出中断时的环境保护,恢复等对性能有要求的地方。
ARM指令集可以分为六大类,分别为数据处理指令 、Load/Store指令 、跳转指令 、程序状态寄存器处理指令 、协处理器指令 和异常产生指令 。
ARM 指令使用的基本格式如下:
〈opcode〉{〈cond〉}{S} 〈Rd〉,〈Rn〉{,〈operand2〉}
opcode 操作码;指令助记符,如LDR、STR等。
cond 可选的条件码;执行条件,如EQ、NE等。
S 可选后缀;若指定“S”,则根据指令执行结果更新CPSR中的条件码。
Rd 目标寄存器。
Rn 存放第1操作数的寄存器。
operand2 第2个操作数
arm的寻址方式如下:
立即寻址
寄存器寻址
寄存器间接寻址
基址加偏址寻址
堆栈寻址
块拷贝寻址
相对寻址
这里不作详细描述,可以查阅相关文档。
数据处理指令
Load/Store 指令
程序状态寄存器与通用寄存器之间的传送指令
转移指令
异常中断指令
协处理器指令
1、 相对跳转指令:b、bl
其中bl除了跳转之外,还将返回地址保存在lr寄存器中。这两条指令的可跳转范围是当前指令的前后32MB。它们是位置无关的指令。
b funa
....
funa:
b funb
....
funb:
....
2、数据传送指令mov,地址读取伪指令ldr
mov指令可以把一个寄存器的值赋给另外一个寄存器,或者把一个常数赋给寄存器。
mov r1, r2
/*上面是r1 = r2*/
mov r1,#1024
/*r1 = 1024*/
mov传送的常数必须能用立即数来表示。当不能用立即数表示时,可以用ldr命令来赋值。
ldr是伪命令,不是真实存在的指令,编译器会把它扩展成真正的指令;如果该常数能用“立即数”来表示,则使用mov指令,否则编译时将该常数保存在某个位置,使用内存读取指令把它读出来。
ldr r1, = 1024
/*r1 = 1024*/
3、内存访问指令 ldr、str、ldm、stm
ldr既可以指地址读取伪指令,也可以是内存访问指令。当他的第二个参数前面有'='时标伪指令,否则表内存访问指令。
ldr指令从内存中读取数据到寄存器,str指令把寄存器的指存储到内存中,他们的操作数都是32位的。
ldr r1, [r2, #4] /*将地址为r2+4的内存单元数据读取到r1中*/
ldr r1,[r2] /*将地址为r2的内存单元数据读取到r1中*/
ldr r1,[r2], #4/*将地址为r2的内存单元数据读取到r1中,然后r2=r2+4*/
str r1 ,[r2, #4]/*将r1的数据保存到地址为r2+4的内存单元中*/
str r1, [r2]/*将r1的数据保存到地址为r2的内存单元中。*/
str r1, [r2],#4/*将r1的数据保存到地址为r2的内存单元,然后r2= r2+4*/
批量数据加载/存储指令LDM(或 STM)指令的格式为:
LDM(或 STM){条件}{类型} 基址寄存器{!},寄存器列表{∧}
LDM(或 STM)指令用于从由基址寄存器所指示的一片连续存储器到寄存器列表所指示的多个寄存器之间传送数据,该指令的常见用途是将多个寄存器的内容入栈(SDM)或出栈(LDM)。其中,{类型}为以下几种情况:
IA 每次传送后地址加 1;
IB 每次传送前地址加 1;
DA 每次传送后地址减 1;
DB 每次传送前地址减 1;
FD 满递减堆栈;
ED 空递减堆栈;
FA 满递增堆栈;
EA 空递增堆栈;
ldmia r0!, {r3-r10} /*将基址寄存器r0开始的连续8个地址单元的值分别赋给r3,r4,r5,r6,r7,r8,r9,r10,注意的是r0指定的地址每次赋一次r0会加1,
指向下一个地址单元*/
stmia r1!, {r3-r10} /*跟上面指令功能相反,将寄存器r3到r10的值依次赋值给r1指定的地址单元,每次赋值一次r1就加1*/
4,CMP 指令的格式
CMP{条件} 操作数 1,操作数 2
CMP 指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行比较,同时更新 CPSR 中条件标志位的值。该指令进行一次减法运算,但不存储结果,只更改条件标志位。标志位表示的是操作数 1 与操作数 2 的关系(大、小、相等),例如,当操作数 1 大于操作操作数 2,则此后的有 GT 后缀的指令将可以执行。
指令示例:
CMP R1,R0 ;将寄存器R1的值与寄存器R0的值相减,并根据结果设置CPSR的标志位
CMP R1,#100 ;将寄存器R1的值与立即数100相减,并根据结果设置CPSR的标志位
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop /*比较r0和r2的值,如果不相等则重新跳到copy_loop处*/
5,ORR 指令的格式
ORR{条件}{S} 目的寄存器,操作数 1,操作数 2
ORR 指令用于在两个操作数上进行逻辑或运算,并把结果放置到目的寄存器中。操作数 1应是一个寄存器,操作数 2 可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于设置操作数 1 的某些位。
指令示例:
ORR R0,R0,#3 ; 该指令设置R0的0、1位,其余位保持不变。
6,BIC 指令的格式
BIC{条件}{S} 目的寄存器,操作数 1,操作数 2
BIC指令用于清除操作数1 的某些位,并把结果放置到目的寄存器中。操作数 1 应是一个寄存器,操作数 2 可以是一个寄存器,被移位的寄存器,或一个立即数。操作数 2 为 32 位的掩码,如果在掩码中设置了某一位,则清除这一位。未设置的掩码位保持不变。
指令示例:
BIC R0,R0,#%1011 ; 该指令清除 R0 中的位 0、1、和 3,其余的位保持不变。
7、程序状态寄存器的访问指令msr,mrs
ARM指令中有两条指令,用于在状态寄存器和通用寄存器之间传送数据。修改状态寄存器一般是通过“读取-修改-写回”三个步骤的操作来实现的。 这两条指令分别是:
状态寄存器到通用寄存器的传送指令(MRS)
通用寄存器到状态寄存器的传送指令(MSR)
其汇编格式如下:
MRS{<cond>} Rd,CPSR|SPSR
其汇编格式如下:
MSR{<cond>} CPSR_f | SPSR_f,#<32-bit immediate>
MSR{<cond>} CPSR_<field> | SPSR_<field>,Rm
msr cpsr, r0 /*复制r0到cpsr中*/
mrs r0, cpsr /*复制cpsr到r0中*/
例:下面这段代码是在u-boot中将cpu设置为SVC32模式
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3 /*通过这两条指令可以看到将后面5位设置为10011也就是管理模式,将6,7位设置为1也就是禁止IRQ和FIQ*/
msr cpsr,r0
可以来看看程序状态寄存器CPSR中各个位分别表示什么含义:
CPSR_f或SPSR_f CPSR_s或SPSR_s CPSR_x或SPSR_x CPSR_c或SPSR_c
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
N Z C V - - - - - - - - - - - - - - - - - - - - I F T M4 M3 M2 M1 M0
负数或小于 零 进位或借位或扩展 溢出 保留位 IRQ禁止1禁0允许 FIQ禁止1禁止0允许 状态位0ARM1
Thumb 模式位
10000-0x0010-用户
10001-0x0011- 快速中断
10010-0x0012-中断
10011-0x0013-管理
10111-0x0017-未定义
11111-0x001F- 系统
8,MCR 指令的格式
MCR{条件} 协处理器编码,协处理器操作码1,源寄存器,目的寄存器1,目的寄存器2,协处
理器操作码2。
MCR 指令用于将ARM 处理器寄存器中的数据传送到协处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。其中协处理器操作码1 和协处理器操作码2 为协处理器将要执行的操作,源寄存器为ARM 处理器的寄存器,目的寄存器1 和目的寄存器2 均为协处理器的寄存器。
指令示例:
MCR P3 , 3 , R0 , C4 , C5 , 6 ;该指令将 ARM 处理器寄存器 R0 中的数据传送到协处理器 P3 的寄存器 C4 和 C5 中。
MRC 指令的格式
MRC{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理
器操作码2。
MRC 指令用于将协处理器寄存器中的数据传送到ARM 处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。其中协处理器操作码1 和协处理器操作码2 为协处理器将要执行的操作,目的寄存器为ARM 处理器的寄存器,源寄存器1 和源寄存器2 均为协处理器的寄存器。
指令示例:
MRC P3 , 3 , R0 , C4 , C5 , 6 ;该指令将协处理器 P3 的寄存器中的数据传送到 ARM 处理器寄存器中.
例:这是u-boot中一段代码
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */将r0的值也就是0赋值给协处理器p15的c7寄存器
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
有必要来看看arm中CP15协处理器中各个寄存器的意义:
寄存器编号
基本作用
在 MMU 中的作用
在 PU 中的作用
0
ID 编码(只读)
ID 编码和 cache 类型
1
控制位(可读写)
各种控制位
2
存储保护和控制
地址转换表基地址
Cachability 的控制位
3
存储保护和控制
域访问控制位
Bufferablity 控制位
4
存储保护和控制
保留
保留
5
存储保护和控制
内存失效状态
访问权限控制位
6
存储保护和控制
内存失效地址
保护区域控制
7
高速缓存和写缓存
高速缓存和写缓存控制
8
存储保护和控制
TLB 控制
保留
9
高速缓存和写缓存
高速缓存锁定
10
存储保护和控制
TLB 锁定
保留
11
保留
12
保留
13
进程标识符
进程标识符
14
保留
15
因不同设计而异
因不同设计而异
因不同设计而异
主要来看看里面c7和c8两个寄存器的意思:
CP15 的 C7 寄存器用来控制 cache 和写缓存,它是一个只写寄存器,读操作将产生不可预知的后果。
CP15 的 C8 寄存器用来控制清除 TLB 的内容,是只写寄存器,读操作将产生不可预知的后果。
9、 其它伪指令
常见如下:
.extern main
.text
.global _start
_start:
“.extern”定义一个外部符号,上面的代码表示本文件中引用的main是一个外部函数。
“.text”表示下面的语句都属于代码段。
“.global”将本文件中的某个程序标号定义为全局的。
说说这个.word伪指令的作用。
.word expression就是在当前位置放一个word型的值,这个值就是expression
举例来说,
_rWTCON: .word 0x15300000
就是在当前地址,即_rWTCON处放一个值0x15300000
翻译成intel的汇编语句就是:
_rWTCON dw 0x15300000
就是在当前位置放个expression的值。
全局变量只能通过地址间接调用,为了访问C语言中全局变量,首先要通过extern伪指令引入全局变量,然后将其地址装入寄存器中。
对于unsigned char类型,使用LDRB/STRB访问;
对于unsigned short类型,使用LDRH/STRH访问;
对于unsigned int类型,使用LDR/STR访问;
对于char类型,使用LDRSB/STRSB访问;
对于short类型,使用LDRSH/STRSH访问;
例子:
.text
.global asmsubroutine
.extern globvar
asmsubroutine:
LDR R1,=globvar
LDR R0,[R1]
ADD R0,R0,#2
STR R0,[R1]
MOV PC,LR
.end
2.C程序调用汇编程序
C程序调用汇编程序首先通过extern声明要调用的汇编程序模块,声明中形参个数要与汇编程序模块中需要的变量个数一致,且参数传递要满足ATPCS规则,然后在C程序中调用。
例子:
#include<stdio.h>
extern void *strcopy(char*d,char*s);//模块声明
int main()
{
char*srcstr="first";
char*dststr="second";
strcopy(dststr,srcstr);//汇编模块调用;
}
.text
.global strcopy
Strcopy:
LDRB R2,[R1],#1
STRB R2,[R0],#1
CMP R2,#0
BNE Sstcopy
MOV PC,LR
.end
汇编程序调用C程序
在调用之前必须根据C语言模块中需要的参数个数,以及ATPCS参数规则,完成参数传递,即前四个参数通过R0-R3传递,后面的参数通过堆栈传递,然后再利用B、BL指令调用。
例子:
int g(int a,int b,int c,int d,int e)//C语言函数原型
{
return(a+b+c+d+e);
}
汇编语言完成是求i+2i+3i+4i+5i的结果;
.global _start
.text
_start:
.extern g ;引入c程序
STR LR,{SP,-#4}!;保存PC
ADD R1,R0,R0
ADD R2,R1,R0
ADD R3,R1,R2
STR R3,{SP,#-4}!
ADD R3,R1,R1
BL g ;调用C函数g
ADD SP,SP,#4
LDR PC,[SP],#4
.end
return(0);
{
int k=a,j=b,m=c;
}
GCC反汇编:
00000064 :
mov ip, sp //IP=SP;保存SP
stmdb sp!, {fp, ip, lr, pc} //先对SP减4,再对fp,ip,lr,pc压栈。---------1
sub fp, ip, #4 ; 0x4 //fp=ip-4;此时fp指向栈里面的“fp”
sub sp, sp, #24 ; 0x18 //分配空间
str r0, [fp, #-28] //
str r1, [fp, #-32] //
str r2, [fp, #-36] //参数压栈
ldr r3, [fp, #-28] //
str r3, [fp, #-24] //
ldr r3, [fp, #-32] //
str r3, [fp, #-20] //
ldr r3, [fp, #-36] //
str r3, [fp, #-16] //
sub sp, fp, #12 ; 0xc //sp=fp-12;此时sp指向栈里面的lr
ldmia sp, {fp, sp, pc} //弹栈pc=lr,sp=ip,fp=fp。然后地址加4---------1
汇编基础:
stmdb sp!, {fp, ip, lr, pc} //sp=sp-4,sp=pc;先压PC
//sp=sp-4,sp=lr;再压lr
//sp=sp-4,sp=ip;再压ip
//sp=sp-4,sp=fp;再压fp
ldmia sp, {fp, sp, pc} //和stmdb成对使用,
//fp=sp,sp=sp+4;先弹fp
//sp=sp,sp=sp+4;先弹sp,此处的弹出不会影响sp,因为ldmia是一个机器周期执行完的。
//pc=sp,sp=sp+4;先弹pc
LDRH R0, [R13, #0xC] //加载无符号半字数据,即低16位
LDRB R0, [R13, #0x4] //加载一字节数据,即低8位。
注意:R11=fp;R12=ip;R13=SP;R14=LR;R15=PC;R0,R1,R2用于传递参数和存放函数返回值。
注意;低地址的寄存器被压入低地址内存中,也就是说如果向下增长,高地址寄存器先压,向上增长测试低地址先压。
注意:根据“ARM-thumb 过程调用标准”:
1, r0-r3 用作传入函数参数,传出函数返回值。在子程序调用之间,可以将 r0-r3 用于任何用途。被调用函数在返回之前不必恢复 r0-r3。---如果调用函数需要再次使用 r0-r3 的内容,则它必须保留这些内容。
2, r4-r11 被用来存放函数的局部变量。如果被调用函数使用了这些寄存器,它在返回之前必须恢复这些寄存器的值。
3, r12 是内部调用暂时寄存器 ip。它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复 r12。
4,寄存器 r13 是栈指针 sp。它不能用于任何其它用途。sp 中存放的值在退出被调用函数时必须与进入时的值相同。
5,寄存器 r14 是链接寄存器 lr。如果您保存了返回地址,则可以在调用之间将 r14 用于其它用途,程序返回时要恢复
6,寄存器 r15 是程序计数器 PC。它不能用于任何其它用途。
7,在中断程序中,所有的寄存器都必须保护,编译器会自动保护R4~R11,所以一般你自己只要在程序的开头
sub lr,lr,#4
stmfd sp!,{r0-r3,r12,lr};保护R0~R3,R12,LR就可以了,除非你用汇编人为的去改变R4~R11的值。(具体去看UCOS os_cpu_a.S中的IRQ中断的代码)
1、相对跳转指令: b 、 bl
不同之处在于: bl 指令除了跳转之外,还将返回地址( bl的下一条指令的地址)保存在lr寄存器中。
跳转范围:当前指令的前后32M。
它们是与位置无关的指令。
示例:
b fun1
......
fun1:
bl fun2
......
fun2:
......
2、数据传送指令: mov ,地址读取伪指令:ldr
mov 指令可以把一个寄存器的值赋给另一个寄存器,或者把一个常数赋给寄存器。
例:
mov r1, r2
mov r1, #4096
mov 指令传送的常数必须能用立即数来表示。
当不知道一个数能否用立即数来表示时,可以使用 ldr 命令来赋值。 ldr 是伪指令,它不是真实存在的指令,编译器会把它扩展成真正的指令:如果该常数能用立即数来表示,则使用 mov指令;否则编译时将该常数保存在某个位置,使用内存读取指令把它读出来。
例:
ldr r1, =4097
ldr 本意为“大范围的地址读取伪指令”,以下是获得代码的绝对地址:
例:
ldr r1, =label
label:
......
3、内存访问指令: ldr 、 str 、 ldm 、 stm
ldr 指令既可能是大范围的地址读取伪指令,也可能是内存访问指令。当它的第二个参数前面有 “ = ”时,表示伪指令,否则表示内存访问指令。
ldr 指令是从内存中读取数据到寄存器,str指令把寄存器的值存储到内存中,它们操作的数据都是32位的。
例:
ldr r1, [r2, #4] // 将地址为r2+4的内存单元数据读取到r1中
ldr r1, [r2] // 将地址为r2的内存单元数据读取到r1中
ldr r1, [r2], #4 // 将地址为r2的内存单元数据读取到r1中,然后r2=r2+4
str r1, [r2, #4] // 将r1的数据保存到地址为r2+4的内存单元中
str r1, [r2] // 将r1的数据保存到地址为r2的内存单元中
str r1, [r2], #4 // 将r1的数据保存到地址为r2的内存单元中,然后r2=r2+4
ldm 和 stm 属于批量内存访问指令,只用一条指令就可以读写多个数据。格式为:
ldm {cond}<addressing_mode> <rn>{ ! } <register list>{ ^ }
stm {cond}<addressing_mode> <rn>{ ! } <register list>{ ^ }
其中,{cond} 表示指令的执行条件有:
条件码(cond)
助记符
含义
cpsr中条件标志位
0000
eq
相等
Z = 1
0001
ne
不相等
Z = 0
0010
cs/hs
无符号数大于/等于
C = 1
0011
cc/lo
无符号数小于
C = 0
0100
mi
负数
N = 1
0101
pl
非负数
N = 0
0110
vs
上溢出
V = 1
0111
vc
没有上溢出
V = 0
1000
hi
无符号数大于
C = 1 或 Z = 0
1001
ls
无符号数小于等于
C = 0 或 Z = 1
1010
ge
带符号数大于等于
N = 1, V = 1 或 N = 0, V = 0
1011
lt
带符号数小于
N = 1, V = 0 或 N = 0, V = 1
1100
gt
带符号数大于
Z = 0 且 N = V
1101
le
带符号数小于/等于
Z = 1 或 N! = V
1110
al
无条件执行
-
1111
nv
从不执行
-
大多数ARM指令都可以条件执行,即根据cpsr寄存器中的条件标志位决定是否执行该指令:如果条件不满足,该指令相当于一条nop指令。每条ARM指令包含4位的条件码域,这表明可以定义16个执行条件。
cpsr条件标志位N、Z、C、V分别表示Negative、Zero、Carry、oVerflow。
<addressing_mode> 表示地址变化模式,有4种方式:
ia (Increment After) :事后递增方式。
ib (Increment Before) :事先递增方式。
da (Decrement After) :事后递减方式。
db (Decrement Before) :事先递减方式。
<rn> 中保存内存的地址,如果后面加上感叹号,指令执行后,rn 的值会更新,等于下一个内存单元的地址。
<register list> 表示寄存器列表,对于 ldm 指令,从 <rn> 所对应的内存块中取出数据,写入这些寄存器;对于 stm 指令,把这些寄存器的值写入<rn> 所对应的内存块中。
{^} 有两种含义:
如果 <register list> 中有 pc寄存器 ,它表示指令执行后,spsr寄存器的值将自动复制到cpsr寄存器中——这常用于从中断处理函数中返回;
如果 <register list> 中没有 pc寄存器 ,{^} 表示操作的是用户模式下的寄存器,而不是当前特权模式的寄存器。
例:
HandleIRQ: @中断入口函数
sub lr, lr, #4 @计算返回地址
stmdb sp!, { r0 - r12, lr } @保存使用的寄存器
@r0 - r12, lr被保存在sp表示的内存中
@“!”使得指令执行后sp = sp - 14 * 4
ldr lr, =int_return @设置调用IRQ_Handle函数后的返回地址
ldr pc, =IRQ_Handle @调用中断分发函数
int_return:
ldmia sp!, { r0 - r12, pc }^ @中断返回,“^”表示将spsr的值复制到cpsr
@于是从irq模式返回被中断的工作模式
@“!”使得指令执行后sp = sp + 14 * 4
4、加减指令: add 、 sub
例:
add r1, r2, #1 // r1 = r2 + 1
sub r1, r2, #1 // r1 = r2 - 1
5、程序状态寄存器的访问指令: msr 、 mrs
ARM处理器有一个程序状态寄存器(cpsr),它用来控制处理器的工作模式、设置中断的总开关。
例:
msr cpsr, r0 // 复制r0到cpsr中
mrs r0, cpsr // 复制cpsr到r0中
6、其他伪指令
.extern : 定义一个外部符号(可以是变量也可以是函数)
.text : 表示现在的语句都属于代码段
.global : 将本文件中的某个程序标号定义为全局的
- ARM_汇编
- 2-ARM_流水线_汇编
- arm_裸奔
- ARM_如何压栈
- 汇编
- 汇编
- 汇编
- 汇编
- 汇编
- 汇编
- 汇编
- 汇编
- 汇编
- 汇编
- 汇编
- 汇编
- 汇编
- 汇编
- 学英语的软件!
- java的hashcode(结合hashset讲解)
- 移动办公解决方案
- OpenWrt学习笔记_OpenWrt编译与烧写
- Apache Thrift - 可伸缩的跨语言服务开发框架
- ARM_汇编
- 对未来网(Future Internet)的一些看法
- 黑马程序员_源自梦想 -->Dos及java常用命令
- hdu4578
- jsp页面日期处理
- android -- 蓝牙 bluetooth (二) 打开蓝牙
- App开源项目收集和汇总:App Store地址和源代码(一)
- 关于DropDownList控件的SelectIndexChanged事件无法触发的终极解决方案
- oracle impdp的table_exists_action详解