本文为转载文章,简单易懂的介绍了 ARM 汇编指令,点击标题见原文链接

ARM 关键几个寄存器

ARM处理器共有37个寄存器。其中包括:31个通用寄存器,包括程序计数器(PC)在内。这些寄存器都是32位寄存器。以及6个32位状态寄存器。但目前只使用了其中12位。ARM处理器共有7种不同的处理器模式,在每一种处理器模式中有一组相应的寄存器组。任意时刻(也就是任意的处理器模式下),可见的寄存器包括15个通用寄存器(R0~R14)、一个或两个状态寄存器及程序计数器(PC)。在所有的寄存器中,有些是各模式共用的同一个物理寄存器;有一些寄存器是各模式自己拥有的独立的物理寄存器。

图中缩写如下:

R:Register;寄存器
PC:Program Counter;程序计数器
CPSR:Current Program Status Register;当前程序状态寄存器
SPSR:Saved Program Status Register;保存的程序状态寄存器
SP:Stack Pointer;数据栈指针
LR:Link Register;连接寄存器
SB:静态基址寄存器
SL:数据栈限制指针
FP:帧指针
IP:Intra-Procedure-call Scratch Register;内部程序调用暂存寄存器

PC(程序计数器) = windows 中 EIP (程序计数器 R15)

处理器要执行的程序(指令序列)都是以二进制代码序列方式预存储在计算机的存储器中,处理器将这些代码逐条地取到处理器中再译码、执行,以完成整个程序的执行。为了保证程序能够连续地执行下去,CPU必须具
有某些手段来确定下一条取指指令的地址。程序计数器(PC)正是起到这种作用,所以通常又称之为‘指令计数器’。CPU 总是按照 PC 的指向对指令序列进行取指、译码和执行,也就是说,最终是PC 决定了程序运行流向。故而,程 序计数器(PC )属于特别功能寄存器范畴,不能自由地用于存储其他运算数据。

在程序开始执行前,将程序指令序列的起始地址,即程序的第一条指令所在的内存单元地址送入 PC,CPU 按照 PC 的指示从内存读取第一条指令(取指)。当执行指令时,CPU 自动地修改 PC 的内容,即每执行一条指令 PC 增加一个量,这个量等于指令所含的字节数(指令字节数),使 PC 总是指向下一条将要取指的指令地址。由于大多数指令都是按顺序来执行的,所以修改PC 的过程通常只是简单的对PC 加“指令字节数”。当程序转移时,转移指令执行的最终结果就是要改变PC的值,此PC值就是转去的目标地址。处理器总是按照 PC 指向取指、译码、执行,以此实现了程序转移。

堆栈指针寄存器 SP (寄存器 R13)

ARM处理器中通常将寄存器R13作为堆栈指针(SP)。ARM处理器针对不同的模式,共有 6 个堆栈指针(SP),其中用户模式和系统模式共用一个SP,每种异常模式都有各自专用的 R13 寄存器(SP)。它们通常指向各模式所对应的专用堆栈,也就是 ARM 处理器允许用户程序有六个不同的堆栈空间,ARM 处理器中的 R13 被用作 SP。当不使用堆栈时,R13 也可以用做通用数据寄存器。

由于处理器的每种运行模式均有自己独立的物理寄存器 R13,在用户应用程序的初始化部分,一般都要初始化每种模式下的 R13,使其指向该运行模式的栈空间。这样,当程序的运行进入异常模式时,可以将需要保护的寄存器放入 R13 所指向的堆栈,而当程序从异常模式返回时,则从对应的堆栈中恢复,采用这种方式可以保证异常发生后程序的正常执行。

当执行子程序调用指令(BL)时,R14 可得到 R15 (程序计数器PC)的备份。在每一种运行模式下,都可用 R14 保存子程序的返回地址,当用BL或BLX指令调用子程序时,将PC的当前值复制给 R14,执行完子程序后,又将 R14 的值复制回 PC,即可完成子程序的调用返回。以上的描述可用指令完成。

1
2
3
4
5
6
7
8
执行以下任意一条指令:
MOV PC,LR
BX LR
在子程序入口处使用以下指令将R14存入堆栈:
STMFD SP!,{,LR}
对应的,使用以下指令可以完成子程序返回:
LDMFD SP!,{,PC}
R14 也可作为通用寄存器。

ARM汇编指令总结


总结目的是为了看懂ARM返汇编程序含义。如果是抱着来看这篇blog的盆友,希望可以帮到你们;如果有错误,请多指出。谢谢!


ARM指令的一般格式

arm指令字长为固定的32位。一条典型的arm指令编码格式如下:

一条典型的ARM指令语法格式如下所示:

参数
opcode:指令操作符编码

cond:决定指令的操作是否影响CPSR的值

S:决定指令操作是否影响CPSR的值

Rd:目标寄存器编码

Rn:包含第1个操作数的寄存器编码

shifter_operand表示第2个操作数


汇编指令执行条件

大多数ARM指令都可以条件执行,即根据cpsr寄存器中的条件标志位决定是否执行该指令:如果条件不满足,该指令相当于一条nop执行。

每条ARM指令包含4位条件码域,这表明可以定义16个执行条件。这16个条件码和他们的助记符(标记)如下:


操作数的运算处理方式

一共有下面9种格式:

  • [< Rn>, #+/-]
  • [< Rn>, +/-< Rm>]
  • [< Rn>, +/-< Rm>, < shift>#]
  • [< Rn>, #+/-]!
  • [< Rn>, +/-< Rm>]!
  • [< Rn>, +/-< Rm>, < shift>#]!
  • [< Rn>], #+/-
  • [< Rn>], +/-< Rm>
  • [< Rn>], +/-< Rm>, < shift>#

总结一下我们可以对上面格式分类:例如:1~3为一类,4~6为一类,7~9为一类。同样的,我们也可以这样分类:1,4,7为一类,2,5,8为一类,3,6,9为一类。

例子示范

通过举例子来说明各个类型的意义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
----------------[< Rn>, #+/-<offset_12>]:---------------
LDR R0,[R1, #4] //将内存单元R1+4中的字读取到R0寄存器中。
-------------------[< Rn>, +/-< Rm>]:-------------------
LDR R0,[R1,R2] //将内存单元R1+R2中的字读取到R0寄存器中
----------[< Rn>, +/-< Rm>, < shift>#<offset_imm>]------
LDR R0,[R1,R2, LSL #2] //将地址单元{R1+R2*4}中的数据读取到R0中
-----------------[< Rn>, #+/-<offset_12>]!--------------
LDR R0,[R1, #4] //将内存单元R1+4中的字读取到R0寄存器中
//同时R1=R1+4
---------------------[< Rn>, +/-< Rm>]!-----------------
LDR R0,[R1, R2]! //将内存单元(R1, R2)中的字读取到R0寄存器中
//同时R1=R1+R2
---------[< Rn>, +/-< Rm>, < shift>#<offset_imm>]!------
LDR R0,[R1, R2, LSL #2]! //将内存单元(R1+R2*4)中的数据读取到R0寄存器中
//同时R1=R1+R2*4
------------------[< Rn>], #+/-<offset_12>--------------
LDR R0, [R1], #4 //将地址为R1的内存单元数据读取到R0中
//然后R1=R1+4
----------------------[< Rn>], +/-< Rm>-----------------
LDR R0, [R1], R2 //将地址为R1的内存单元数据读取到R0中
//然后R1=R1+R2
---------[< Rn>], +/-< Rm>, < shift>#<offset_imm>-------
LDR R0, [R1], R2, LSL #2 //将地址为R1的内存单元数据读取到R0中
//然后R1=R1+R2*4

CPRS各位介绍

CPRS为状态寄存器,下面他各位的功能和代表的含义

这里写图片描述

.word

.word expression就是在当前位置放一个word型的值,这个值就是expression

例子示范

1
2
3
_rWTCON:
.word 0x15300000
//就是在当前地址,即_rWTCON处放一个值0x15300000

.macro

宏定义

参数

1
2
3
4
5
宏名称 .MACRO [形式参数]
........
宏定义语句
........
.ENDM

例子示范

1
2
3
4
5
SWAP_REG .MACRO REG1,REG2 ; swap registers
XCH A, REG1
XCH A, REG2
XCH A, REG1
.ENDM

.global

告诉编译器后续跟的是一个全局可见的名字[可能是变量,也可以是函数名]

.type

.type:用来指定一个符号的类型是函数类型或者是对象类型, 对象类型一般是数据, 格式如下:
.type 符号, 类型描述

@

这个是 GNU 汇编的规范. 就是注释.

A


add

加指令

例子示范

1
add r1,r2,#1 //表示r1=r2+1, 即寄存器r1的值等于寄存器r2的值加上1

adr和adrl

adr伪指令将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中。adrl伪指令比adr伪指令可以读取更大范围的地址,其他功能一样。

参数

1
2
ADR伪指令格式 :ADR{cond} register, expr
ADRL伪指令格式:ADRL{cond} register, expr

例子示范

1
2
3
4
5
adr r2, mem_cfg_val //存储相对位置
adr r1, mem_cfg_val //存储相对位置
mem_cfg_val:
...

align

.align的作用在于对指令或者数据的存放地址进行对齐,有些CPU架构要求固定的指令长度并且存放地址相对于2的幂指数圆整,否则程序无法正常运行,比如ARM。

参考:
http://blog.csdn.net/xingyu19871124/article/details/7333622


B


b和bl

相对跳转指令。两条指令不同之处在于bl指令除了跳转之外,还将返回地址(bl的下一条指定的地址)保存在lr寄存器中。

BL.W表示该指令为32位指令,单独用BL 可能是16位的,也可能是32位,Keil为了区分 反汇编时 用BL.W 表示 32位,BL表示16位。

例子示范

1
2
3
4
b fun1
fun1:
.....

bic

bic(位清除)指令对 Rn 中的值 和 Operand2 值的反码按位进行逻辑“与”运算

1
2
BIC R1, R1, #0x0F
#将R14位清0

C


D


E


equ

equ是类似于宏的作用,相当于#define。

例子示范

1
.equ MEM_CTL_BASE, 0x48000000

F


G


H


I


J


K


L


long

.long:定义4字节数据,

例子示范

1
.long 0x12345678,23876565

ldr

这个指令是一个伪指令,他不是真实存在的指令,编辑器会把它扩展成真正的指令。

ldr指令从内存中读取数据到寄存器。

例子示范

1
2
3
4
5
ldr r1,=4096 //r1 = 4096
ldr r1,[r2,#4] //将地址为r2+4的内存单元数据读取到r1中去
ldr r1,[r2] //将地址为r2的内存单元位数据读取到r1中去
ldr r1,[r2],#4 //将地址为r2的内存单元的数据读取到r1中,然后r2=r2+4

ldm

ldm{cond} {!} < register list> {^}

批量访问内存,内存中批量读取数据到寄存器。从< rn> 对应的内存块中取出数据,写入 < register list>这些寄存器。

参数

  • {cond}表示指定执行条件

  • 表示内存变化的模式:

    1. ia(increment after):事后递增方式
    2. ib(increment before):事先递增方式
    3. da(decrement after):事后递减方式
    4. db(decrement before):事先递减方式
  • < rn>中保存着内存的地址,
  • {!}加上了感叹号,指令执行后,rn的值会更新,等于下一个内存单元地址。
  • < register list>表示寄存器列表,指令中寄存器列表和内存单元对应关系为:编号低得寄存器对应内存中得低地址单元,编号高的寄存器对应内存中的高地址单元。
  • {^}有两个含义:如果< register list>中有pc寄存器,它表示指令执行之后,spsr寄存器的值将自动复制到cpsr寄存器中–这常用于从中断处理函数中返回;如果< register list>中没有pc寄存器,{^}表示操作的是用户模式下的寄存器,而不是当前特权模式的寄存器。

例子示范

1
2
3
4
5
ldmia sp!,{r0-r12,pc}
//中断返回,“^”表示将spsr的值复制到cpsr
//于是从irq模式返回被中断的工作模式
//“!”使得指令执行后,sp=sp+14*4

M


mov

mov指令可以把一个寄存器的值赋给另一个寄存器,或者把一个常数赋值给寄存器。

例子示范

1
2
mov r1,r2 //r1 = r2
mov r1,#4096 //r1 = 4096

msr和mrs

程序状态寄存器访问指令。

ARM处理器中有一个程序状态寄存器(cpsr),他用来控制处理器的工作模式,设置中断的总开关。

例子示范

1
2
msr cpsr,r0 //复制r0cpsr中
mrs r0cpsr //复制cpsr到r0中

mcr和mrc

MCR指令将ARM处理器的寄存器中的数据传送到协处理器的寄存器中。如果协处理器不能成功地执行该操作,将产生未定义的指令异常中断。

MRC指令将协处理器的寄存器中数值传送到ARM处理器的寄存器中。如果协处理器不能成功地执行该操作,将产生未定义的指令异常中断。

格式

1
2
3
MCR{<cond>} p15, 0, <Rd>, <CRn>, <CRm>{,<opcode_2>}
MRC{<cond>} p15, 0, <Rd>, <CRn>, <CRm>{,<opcode_2>}

参数

  1. 为指令执行的条件码。当忽略时指令为无条件执行。MCR2中,为Ob1111,指令为无条件执行指令。

  2. 为协处理器将执行的操作的操作码。对于CP15协处理器来说, 永远为0b000,当不为0b000时,该指令操作结果不可预知。

  3. 作为元寄存器的ARM寄存器,其值被传送到得协处理器寄存器中。

  4. 不能为PC,当其为PC时,指令操作结果不可预知。

  5. 作为目标寄存器的协处理器寄存器,其编号可能为C0,C1….C15。 附加的目标寄存器或者原操作数寄存器,用于区分同一个编号的不同物理寄存器。当指令中不需要提供附加信息时,将C0指定为,否则指令操作结果不可预知。

  6. 提供附加信息,用于区别同一个编号的不同物理寄存器。当指令中指定附加信息时,省略或者将其指定为0,否则指令操作结果不可预知。


N


O


P


Q


R


S


str

str也是一条伪指令。str指令把寄存器的值存储到内存中。

例子示范

1
2
3
str r1,[r2,#4] //将r1的数据保存到地址为r2+4的内存单元中
str r1,[r2] //将r1的数据保存到地址为r2的内存单元中
str r1,[r2],#4 //将r1的数据保存到地址为r2的内存单元中,然后r2=r2+4

stm

stm{cond} {!} < register list> {^}

批量存储数据到内存,寄存器批量存储数据到内存。< register list>这些寄存器中得数据,批量写入到< rn> 对应的内存块中。

参数

  • {cond}表示指定执行条件

  • 表示内存变化的模式:

    1. ia(increment after):事后递增方式
    2. ib(increment before):事先递增方式
    3. da(decrement after):事后递减方式
    4. db(decrement before):事先递减方式
  • < rn>中保存着内存的地址,
  • {!}加上了感叹号,指令执行后,rn的值会更新,等于下一个内存单元地址。
  • < register list>表示寄存器列表,指令中寄存器列表和内存单元对应关系为:编号低得寄存器对应内存中得低地址单元,编号高的寄存器对应内存中的高地址单元。
  • {^}有两个含义:如果< register list>中有pc寄存器,它表示指令执行之后,spsr寄存器的值将自动复制到cpsr寄存器中–这常用于从中断处理函数中返回;如果< register list>中没有pc寄存器,{^}表示操作的是用户模式下的寄存器,而不是当前特权模式的寄存器。

例子示范

1
2
3
4
5
stmdb sp!,{r0-r12,lr}
//保存使用到得寄存器
//r0-r12lr被保存在sp表示的内存中
//“!”使得指令执行后sp=sp-14*4

sub

减指令

例子示范

1
sub r1,r2,#1 //表示r1=r2-1

T


U


V


W


X


Y


Z


ARM 指令注意点:

  1. 一般指令需1个主时钟时间
  2. 每次跳转增加3个主时钟时间(也许是2个,需要确认)
  3. 从RAM中取一个操作数多增加一个主时钟时间,以次类推
  4. 保存一个结果到RAM中多增加一个主时钟时间,以次类推
  5. 访问片内外设,增加一个外设时钟时间 注意伪指令ldr rn,=x需要从ram中取一个操作数。

参考链接:
http://blog.csdn.net/jscese/article/details/46547985
http://blog.csdn.net/smalosnail/article/details/53048784