目录
一、ARM汇编指令集... 1
1、两个概念:指令和伪指令... 1
2、两种不同风格的ARM指令... 1
3、ARM汇编特点... 1
3.1、特点1:LDR/STR架构... 1
3.2、特点2:8种寻址方式... 1
3.3、特点3:指令后缀... 2
3.4、特点4:条件执行后缀(大于、小于、等于)... 3
3.5、特点5:多级指令流水线... 3
4、数据传输与跳转指令... 4
4.1、数据处理指令... 4
4.2、cpsr访问指令... 5
4.3、跳转指令 b & bl & bx. 5
4.4、访存指令(访问内存) ldr/str & ldm/stm & swp. 5
4.5、软中断指令(用软件模拟中断)(用于操作系统)... 6
4.6、立即数... 6
二、协处理器和协处理器指令... 6
1、什么是协处理器... 6
2、协处理器cp15操作指令 mcr & mrc. 6
2.1、操作指令的作用... 6
2.2、操作指令的使用方法... 7
三、ldm/stm与栈的处理... 7
1、为什么需要多寄存器访问指令... 7
2、ldm/stm的8种后缀... 7
3、四种栈... 8
4、!的作用... 8
5、^的作用... 8
6、总结... 9
四、伪指令... 9
1、伪指令的意义... 9
2、gnu汇编中的一些符号... 9
3、常见的gnu伪指令... 9
4、偶尔用到的gnu伪指令... 10
5、最重要的伪指令... 10
6、adr和ldr. 11
注意:
CPU与内存之间的数据交换需要通过CPU中的通用寄存器才可以,所以访问效率会低一些。
Mov表示从寄存器到寄存器,ld和str表示从内存到寄存器。
寄存器寻址 mov r1, r2R1和r2都是寄存器名字,意思是将r2中的内容传送到r1
立即寻址 mov r0, #0xFF00就是立即数寻址,意思是将立即数#0xFF00给r0
寄存器移位寻址 mov r0, r1, lsl #3意思是将r1左移3位后传送个r0(lsl表示左移)
寄存器间接寻址 ldr r1, [r2][r2]表示r2中存储的所指向的内存的地址,意思是将r2中存储的所指向的内存中的数据给r1。
基址变址寻址 ldr r1, [r2, #4]意思是将r2中存储的地址加上4后所指向的地址中的内容给r1。
多寄存器寻址 ldmia r1!, {r2-r7, r12}意思是一次访问7个寄存器,{ r2-r7, r12}表示r2、r3、r4、r5、r6、r7、r12这7个寄存器。而r1!表示一串内存空间,相当于数组头地址,第二个参数有7个寄存器,它就有7个内存空间。
将r1所存地址所代表的内存及其后边的7个连续内存中存储的数据传给7个寄存器。
堆栈寻址 stmfd sp!, {r2-r7, lr}sp是堆栈指针,是一个确定的值。具体和多寄存器寻址类似。
相对寻址 beq flag 以PC为标志来跳转。
在汇编中,“flag:”这种形式的写法叫做标号。用来标记后边指令的地址。这样可以直接跳转到标号出执行。和C语言中的goto一样。
同一指令经常附带不同后缀,变成不同的指令。
经常使用的后缀有:
B(byte)功能不变,操作长度变为8位H(half word)功能不变,长度变为16位S(signed)功能不变,操作数变为有符号,如: ldr ldrb ldrh ldrsb ldrsh
S(S标志)功能不变,影响CPSR标志位如 :mov和movs
mov r0, #0 将0这个数字赋给r0,且r0中的标志位不变。
movs r0, #0 将0这个数字赋给r0,且r0中的Z标志位变为1。
例如:
mov r0,r1 相当于C语言中的r0=r1;
moveq r0,r1 如果eq后缀成立,则直接执行mov r0,r1;如果eq不成立,则此语句直接作废,相当于没有。类似于C语言中的
if ( eq ) { r0=r1; }
注意:
条件后缀是否成立不是取决于本句代码,而是取决于这句代码之前的代码运行的结果。
条件后缀决定了本句代码是否被执行,而不会影响上一句和下一句代码是否执行。
mov(move) mov r1,r0 在两个寄存器之间进行数据传递
mov r1,#oxff 将立即数赋值给寄存器
mov和mvn用法一样,区别是mov是原封不动的传递,而mvn是按位取
反后传递。
例如:r1 = 0x000000ff,然后mov r0, r1 后,r0 = 0xff,但是我mvn r0, r
后,r0=0xffffff00
算术指令 add sub rsb adc sbc rsc 逻辑指令 and orr eor bicand 逻辑与
orr 逻辑或(位或)
eor 裸机异或
bic 位清除指令
bic r0,r1,#0x1f 将r1中的数的bit0到bit4清零后赋值给r0 0x1f =0x0001 1111
比较指令(比较指令用来比较2个寄存器中的数)cmp cmn tst teqcmp cmp r0, r1 判断r0-r1=0,等价于 sub r2, r0, r1 (r2 = r0 - r1)
cmn cmn r0, r1 判断r0+r1=0,等价于 add r0, r1
tst tst r0, #0xf 测试r0的bit0~bit3是否全为0
tst r0,#0x8 测试r0的bit3是否为0。
teq teq r0,r1 对两个数进行EOR(异或),比较是否相等。若两个数
相等,结果为假,否则,结果为真。
注意:比较指令不用后加s后缀就可以影响cpsr中的标志位。
比较指令不需要保存结果,直接访问标志位即可知道比较指令的结果。
乘法指令 mvl mla umull umlal smull smlal前导零计数 clz(基本用不到)mrs r0,cpsr 将cpsr读取到寄存器r0中
msr cpsr,r0 将r0中的数读取到cpsr中。
cpsr和spsr的区别和联系:cpsr是程序状态寄存器,整个SoC中只有1个;而spsr有5个,分别在5种异常模式下,作用是当从普通模式进入异常模式时,用来保存之前普通模式下的cpsr的,以在返回普通模式时恢复原来的cpsr。
swp r1, r2, [r0] 将r0所指向的内存中的值读取到r1中,同时将r2中的值写入到r0所指向的内存中。
swp r1, r1, [r0] 将r0所指向的内存中的值读取到r1中,同时将r1之前的值写入到r0所指向的内存中。
例如:
合法立即数: 0x000000ff 0x00ff0000 0xf000000f
非法立即数: 0x000001ff
mrc用于读取CP15中的寄存器
mcr用于写入CP15中的寄存器
mcr{<cond>} p15, <opcode_1>, <Rd>, <Crn>, <Crm>, {<opcode_2>}
opcode_1:对于cp15永远为0Rd:ARM的普通寄存器,不能是r15,否则结果未知Crn:cp15的寄存器,合法值是c0~c15Crm:cp15的寄存器,一般均设为c0opcode_2:一般省略或为0例子:
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000
bic r0, r0, #0x00000007
orr r0, r0, #0x00000002
orr r0, r0, #0x00000800
mcr p15, 0, r0, c1, c0, 0
举例
stmia sp, {r0 - r12}
说明:
将r0存入sp指向的内存处(假设为0x30001000);然后地址+4(即指向0x30001004),将r1存入该地址;然后地址再+4(指向0x30001008),将r2存入该地址······直到r12内容放入(0x3001030),指令完成。一个访存周期同时完成13个寄存器的读写例子:死循环(相当于C语言中的while(1);)
flag: flag标号,表示一个地址
b flag 跳转到flag本身
b . 跳转到当前指令的地址(这一句相当于上边两行)
# 立即数前面要加#或$,表示这是个立即数例子:
IRQ_STACK_START:
.word 0x0badc0de
其中IRQ_STACK_START为变量名,.word为变量类型,0x0badc0de为变量的值。相当于unsigned int IRQ_STACK_START = 0x0badc0de。
.quad .float .string @ 定义数据(双字,小数,字符串).align 4 @ 以16字节对齐,2的4次方。.align 2 @ 以4字节对齐,2的2次方
.balignl 16 0xabcdefgh @ 16字节对齐填充B表示位填充;align表示对齐;l表示long,以4字节为单位填充;16表示以16字节对齐;0xabcdefgh是用来填充的原料。
例子:
地址
0x00000008 .balignl 16 0xabcdefgh
0x0000000c 0xabcdefgh
0x00000010 下一条指令
说明:
0x00000008为原地址,运行.balignl 16 0xabcdefgh伪指令后,需要16字节对齐,跳转到0x00000010,中间的地址0x0000000c用0xabcdefgh填充。
.equ @ 类似于C中宏定义注意:
ARM中有一个ldr指令,还有一个ldr伪指令。一般都使用ldr伪指令而不
用ldr指令。
如果是指令,立即数前边是#,如果是伪指令,立即数前边是=;涉及到合法/非法立即数,设计到ARM文字池。
例如:
ldr指令 : ldr r0,#0xff 需要编程人员考虑立即数是否合法
ldr伪指令: ldr r0,=0xff 不用考虑立即数是否合法
adr和ldr的差别:
ldr加载的地址在链接时确定,而adr加载的地址在运行时确定;所以我们可以通过adr和ldr加载的地址比较来判断当前程序是否在链接时指定的地址运行。