一、ARM汇编指令集
1. 指令与伪指令
(汇编)指令是CPU机器指令的助记符,经过编译后会得到一串10组成的机器码,可以由CPU读取执行。
(汇编)伪指令本质上不是指令(只是和指令一起卸载代码中),它是编译器环境提供的,目的是用来指导编译过程,经过编译后伪指令最终不会生成机器码。
2、两种不同风格的ARM指令
ARM官方的ARM汇编风格:指令一般用大写、Wingdows中的IDE开发环境(如IDS、MDK等)常用。如:LDR R0,[R1]
GNU风格的ARM汇编:指令一般用小写字母、linux中常用。如ldr r0,[r1]
3、ARM汇编特点1: LDR/STR架构
ARM采用RISC架构,CPU本身不能直接读取内存,而需要将内存中内容加载入CPU中通用寄存器中才能被CPU处理。对内存中的数据进行操作,需要首先将内存中的数据加载到通用寄存器中,然后再进行操作。
ldr (load register)指令将内存的内容加载到通用寄存器
str (store register)指令将寄存器内容存入内存空间。
4、ARM汇编特点2:8种寻址方式
寄存器寻址
mov r1, r2
@寄存器之间的数据传输
立即寻址
mov r0, #0xFF00
@0xFF00就是立即数
寄存器移位寻址
mov r0,r1,lsl #3
@r0 = r1*8
寄存器间接寻址
ldr r1, [r2]
@寄存器穿上[]衣服后,表示寄存器存储的是内存的地址。该命令是将r2指向的内存地址的内容加载到r1.
基址变址寻址
ldr r1,[r2,#4]
@r1 = [r2+4]
多寄存器寻址
ldmia r1!, {r2-r7, r12}
@r1存储的内存地址为起始地址,依次递增取出内存内容,存到r2、r3、...、r7、r12
堆栈寻址
stmfd sp!, {r2-r7, lr}
@
相对寻址
beq flag
flag:
...
5、ARM汇编特点3:指令后缀
同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:
-B(byte)功能不变,操作长度变为8位
-H(Half word)功能不变,长度变为16位
-S(Signed)功能不变,操作数变为有符号
如ldr, ldrb, ldrh, ldrsb, ldrsh
-S(S标志)功能不变,影响CPSR标志位,如mov, movs
6、ARM汇编特点4:条件后缀
-EQ 相等
-NE 不相等
-CS/HS 无符号数大于或等于
-CC/LO 无符号数小于
-MI 负数
-PL 正数或零
-VS 溢出
-VC 没有溢出
-HI 无符号数大于
-LS 无符号数小于或等于
-GE 有符号数大于或等于
-LT 有符号数小于
-GT 有符号数大于
-LE 有符号数小于或等于
-AL 无条件执行(指令默认条件)
-NV 从不执行(不要使用)
moveq r0, r1
@如果eq后缀成立,则直接执行mvo r0, r1; 如果eq不成立则本句代码直接作废,相当于没有。
条件后缀执行注意两点:
-条件后缀是否成立,不是取决于本句代码,而是取决于这句代码之前的代码运行后的结果。
-条件后缀决定了这句代码是否被执行,而不影响其他代码的执行。
7、ARM汇编特点5:多级指令流水线
为增加处理器指令流的速度,ARM使用多级流水线,
以3级流水线举例
ARM
Thumb2
]
PC
PC
取指
从存储器读取指令
PC-4
PC-2
解码
解码指令中用到的寄存器
PC-8
PC-4
执行
CPU执行指令
NOTE: PC指向正在被取指的指令,而非正在执行的指令
8、常用ARM指令1:数据处理指令
-数据传输指令 mov, mvn
-算数指令 add sub rsb adc sbc rsc
-逻辑指令 and orr eor bic
-比较指令 cmp cmn tst teq, 比较指令用来比较两个寄存器的数,不需要后加s后缀就可以影响cpsr中的标志位。
-乘法指令 mvl mla umull umlal smull smlal
-前导零技术 clz
9、常用ARM指令2:cpsr访问指令
mrs: 读取cpsr和spsr
msr:写cpsr和spsr
10、常见ARM指令3:跳转(分支)指令
-b 直接条状,跳转后不会返回
-bl branch and link,跳转前把返回地址放入lr中,以便返回,主要用于调用函数
-bx,跳转的同时切换到ARM模式,一般用于异常处理的跳转
11、常用ARM指令4:访存指令
ldr/str & ldm/stm & swp
swp是读写指令
swp r1, r2, [r0] @将[r0]内存内容读到r1, r2的内容写到[r0]
swp r1, r1, [r0] @r1和[r0]的内容互换
ARM汇编中的立即数
ARM指令都是32位,除了指令标记和操作标记外,本身只能附带很少位数的立即数。因此立即数有合法和非法之分
合法立即数:经过任意位数的移位后,非零部分可以用8位表示的即为合法立即数
12、常用ARM指令5:软中断指令
swi: software interrupt
软中断指令用来实现操作系统中系统调用
13、常用ARM指令6:协处理器cp15指令
-什么是协处理器?
SoC内部另一处理核心,协助主CPU实现某些功能,被主CPU调用执行一定任务
ARM设计上支持多达16个协处理器,但是一般SoC至实现其中的CP15
协处理器和MMU、Cache、TLB等处理相关,功能上和操作系统的虚拟地址映射、cache管理等相关。
14、常用ARM指令6:stm/ldm与栈的处理
-为什么需要多寄存器访问指令
ldr/str每周期只能访问4字节内存,如果需要批量读取、写入内存时太慢,解决方案是stm/ldm
ldm: load register multiple
stm: store register multiple
-举例
stmia sp, {r0-r12}
将r0存入sp指向的内存出;然后地址+4,将r1存入改地址;然后地址再+4,将r2存入改地址……直到r12内容放完,指令完成。
一个访存周期同时完成13个寄存器的读写。
-8种后缀
ia: increase after, 先传输数据,再地址+4
ib: increase before,先地址+4,再传输数据
da: decrase after
db: decrease before
fd::full decrease
ed: empty decrease
fa
ea
-四种栈
空栈:栈指针指向空位,每次存入时可以直接存入然后栈指针移动一格;而取出时需要先移动一格才能取出。
满栈:栈指针指向栈中最后一格数据,每次存入时需要先移动栈指针一格再存入;取出时直接取出,然后再移动栈指针。
增栈:栈指针移动时向地址增加方向移动的栈。
减栈:栈指针移动时向地址减小的方向移动的栈。
- !的作用
ldmia r0, {r2-r3}
@执行该命令后,r0里的值不变
ldmia r0!, {r2-r3}
@执行完该命令后,r0里的值+4
所以感叹号的作用就是r0的值在Ldm过程中发生的增加或减少最后写回到r0去,也就是说ldm时会改变r0的值。
- ^的作用
ldmfd sp!, {r0-r6, pc}
ldmfd sp!, {r0-r6, pc}^
^的作用:在目标寄存器中有pc时,会同时将spsr写入到cpsr
二、 ARM汇编伪指令
1、伪指令的意义
伪指令不是指令,伪指令和指令的根本区别是经过编译后会不会生成机器码。
伪指令的意义自傲与指导编译过程。
伪指令是和具体的编译器相关的,我们使用gnu工具链,因此学习gnu环境下的汇编伪指令。
2、gun汇编中的一些符号
:结尾的符号是标号,用来标记地址,类似于C中定义语句int a; 编译器会给标号分配一个地址。
.点号,表示当前的地址。
b. @代表死循环,与while(1);类似。
3、常见的gnu伪指令
- .globl _start
@给_start外部链接属性,相当于定义了全局属性,其他源文件可以引用。
- .section .text
@指定当前段为代码段
- .ascii .byte .short .long .word @用于定义变量类型,分别表示定义 字符型/字节型/短整型/长整型/字型
- .quad .float .string
@定义数据 八字节/浮点型/字符串型
- .align 4
@以2^4=16字节对齐
- .balignl 16 0xdeadbeaf
@16字节对齐填充,其中b表示位填充, align表示对齐,l表示long,以4字节为单位填充;16表示16字节对齐;0xdeadbeaf是用来填充的原料
- .equ
@类似于C中宏定义
4、偶尔会用到的gnu伪指令
- .end
@表示文件结束
- .include
@头文件包含
- .arm / .code32
@声明以下为arm指令
- .thumb / .code16
@声明一下为thumb指令
5、最重要的几个伪指令
- ldr
大范围的地址加载指令
- adr
小范围的地址加载指令
- adrl 中等范围的地址加载指令
- nop
空操作
6、adr与ldr
- adr编译时会被1条sub或add指令替代,而ldr编译时会被一条mov指令或者文字池方式处理;
- adr总是以PC为基准来表示地址,因此指令本身和运行地址有关,可以用来检测程序当前的运行地址在哪里
- ldr加载的地址和链接时给定的地址有关,由链接脚本决定。