ARM包含6种重要的数据计算处理指令
数据处理指令格式与条件码
条件码
ARM汇编特点3:指令后缀
同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:
B(byte)功能不变,操作长度变为8位
H(half word)功能不变,长度变为16位
S(signed)功能不变,操作数变为有符号
如 ldr ldrb ldrh ldrsb ldrsh
S(S标志)功能不变,影响CPSR标志位
如 mov和movs movs r0, #0
ARM汇编特点4:条件执行后缀
例:Mov r0,r1 //相当于C中的r0=r1;
Movep r0,r1 //如果eq后缀成立,则执行mov r0,r1 如果后缀不成立,则此句做类似于C语言中的 if(eq) {r0 = r1}
注:1、条件后缀是否成立,取决于之间的代码。
2、条件后缀是否被执行,不影响上一句或下一句的代码。
令通过MOV,剩下与C语言中的数据操作是
数据传输指令mov mvn
例:1、mov r0,r1 @两个寄存器之间数据传递
2、mvn r1,#0xff @将立即数赋值给寄存器
Mvn和mov用法一样,区别是mov原封不动传递,而mvn是按位取反传递
譬如r1 = 0x000000ff,然后mov r0, r1 后,r0 = 0xff但是mvn r0, r1后,r0=0xffffff00
逻辑指令, AND ORR EOR BIC
逻辑指令and(逻辑与) orr(逻辑或) eor(逻辑异或) bic(位清除指令)
ORN(逻辑或非,进Thumb-2)
bicr0,r1,#0x1f@ 将r1中的数的bit0到bit4清零后赋值给r0
乘法指令, MUL MLA UMULL UMLAL SMULL SMLAL
算术指令add(加法) sub(减法) rsb(反向减法) adc(带进位加法) sbc(带进位减法) rsc(带进位反向减法,进ARM)
cmpcmp r0, r1 等价于 sub r2, r0, r1 (r2 = r0 - r1)
cmncmn r0, r1 等价于 add r0, r1
tsttst r0, #0xf@测试r0的bit0~bit3是否全为0
teq
比较指令用来比较2个寄存器中的数
注意:比较指令不用后加s后缀就可以影响cpsr中的标志位。
常用ARM指令2:cpsr访问指令
mrs & msr
mrs用来读psr,msr用来写psr
CPSR(程序状态寄存器)寄存器比较特殊,需要专门的指令访问,这就是mrs和msr。
常用ARM指令3:跳转(分支)指令
b & bl & bx
b 直接跳转(就没打开算返回)
bl branch and link,跳转前把返回地址放入lr(链接寄存器)中,以便返回,以便用于函数调用
bx跳转同时切换到ARM模式,一般用于异常处理的跳转。
常用ARM指令4:访存指令
ldr/str &ldm/stm & swp
Ldr 含义理解: Load from memory into register
str 含义理解:Store from a register into memory
Ldm 含义理解:Load from memory into register
Stm 含义理解:Store from a register into memory
单个字/半字/字节访问 ldr/str
多字批量访问 ldm/stm
swp r1, r2, [r0]
swp r1, r1, [r0]
ARM汇编中的立即数
合法立即数与非法立即数
ARM指令都是32位,除了指令标记和操作标记外,本身只能附带很少位数的立即数。因此立即数有合法和非法之分。
合法立即数:经过任意位数的移位后非零部分可以用8位表示的即为合法立即数
常用ARM指令5:软中断指令
swi(software interrupt)
软中断指令用来实现操作系统中系统调用
1.2.18.ARM汇编指令集4
协处理器cp15操作指令
mcr & mrc
mrc用于读取CP15中的寄存器
mcr用于写入CP15中的寄存器
什么是协处理器
SoC内部另一处理核心,协助主CPU实现某些功能,被主CPU调用执行一定任务。
ARM设计上支持多达16个协处理器,但是一般SoC只实现其中的CP15.(cp:coprocessor)
协处理器和MMU、cache、TLB等处理有关,功能上和操作系统的虚拟地址映射、cache管理等有关。
MRC & MCR的使用方法
mcr{
opcode_1:对于cp15永远为0
Rd:ARM的普通寄存器
Crn:cp15的寄存器,合法值是c0~c15
Crm:cp15的寄存器,一般均设为c0
opcode_2:一般省略或为0
举例(来自于uboot)
mrc p15, 0, r0, c1, c0, 0
orrr0, r0, #1
mcr p15, 0, r0, c1, c0, 0
协处理器学习要点
不必深究,将uboot中和kernel中起始代码中的一般操作搞明白即可。
只看一般用法,不详细区分参数细节,否则会陷入很多复杂未知中。
关键在于理解,而不在于记住。
1.2.19.ARM汇编指令集5
为什么需要多寄存器访问指令
ldr/str每周期只能访问4字节内存,如果需要批量读取、写入内存时太慢,解决方案是stm/ldm
ldm(load register mutiple)
stm(store register mutiple)
举例(uboot start.S 537行)
stmia sp, {r0 - r12}
将r0存入sp指向的内存处(假设为0x30001000);然后地址+4(即指向0x30001004),将r1存入该地址;然后地址再+4(指向0x30001008),将r2存入该地址······直到r12内容放入(0x3001030),指令完成。
一个访存周期同时完成13个寄存器的读写
8种后缀
ia(increase after)先传输,再地址+4
ib(increase before)先地址+4,再传输
da(decrease after)先传输,再地址-4
db(decrease before)先地址-4,再传输
fd(full decrease)满递减堆栈
ed(empty decrease)空递减堆栈
fa(full ascending) 满递增堆栈
ea(empty ascending)空递增堆栈
四种栈
空栈:栈指针指向空位,每次存入时可以直接存入然后栈指针移动一格;而取出时需要先移动一格才能取出
满栈:栈指针指向栈中最后一格数据,每次存入时需要先移动栈指针一格再存入;取出时可以直接取出,然后再移动栈指针
增栈:栈指针移动时向地址增加的方向移动的栈
减栈:栈指针移动时向地址减小的方向移动的栈
!的作用
ldmia r0, {r2 - r3}
ldmia r0!, {r2 - r3}
感叹号的作用就是r0的值在ldm过程中发生的增加或者减少最后写回到r0去,也就是说ldm时会改变r0的值。
^的作用
ldmfd sp!, {r0 - r6, pc}
ldmfd sp!, {r0 - r6, pc}^
^的作用:在目标寄存器中有pc时,会同时将spsr写入到cpsr,一般用于从异常模式返回。
总结
批量读取或写入内存时要用ldm/stm指令
各种后缀以理解为主,不需记忆,最常见的是stmia和stmfd
谨记:操作栈时使用相同的后缀就不会出错,不管是满栈还是空栈、增栈还是减栈
2、6类指令:
前导零计数。
程序状态寄存器访问指令:MRS MSR
跳转指令: B BL BX
访存指令: LDR/STR LDM/STM SWP
异常中断产生指令: SWI
协处理器指令: mcr mrc
3、LDR/STR架构
LDR LDRB LDRH LDRSB LDRSH
STR STRB STRH STRSB STRSH
LDM/STM
SWP SWPB
4、指令后缀:B H S
5、条件后缀:
EQ/NE:等于/不等于(equal / not equal)
HS/LO:无符号数高于或等于/无符号数小于(higher or same/lower)
HI/LS:无符号数高于/无符号数低于或等于(higher/lower or same)
GE/LT:有符号数大于或等于/有符号数小于(greater or equal/less than)
GT/LE:有符号数大于/有符号数小于或等于(greater than/less or equal)
MI/PL:负/非负
VS/VC:溢出/不溢出(overflow set / overflow clear)
CS/CC:进位/无进位(carry set / carry clear
6、ldm/stm与栈的操作
ldmfd/stmfd
ldmia/stmia
7、合法、非法立即数
伪指令(gnu汇编伪指令)
ARM汇编程序常用的编译环境有2个:ARMASM(ARM官方汇编格式,Windows中常用,如ADS、MDK,指令一般用全大写)和GNU ARM ASM(开源社区常用,如uboot、linux,指令一般全小写)。
@开头的之后部分是注释(@不一定非在行首,类似于C中的//)
#开头的整行注释。一般整行注释用#而前面是代码后面写注释的用@
:结尾的是标号
标号+f,表示在引用处向前找;标号+b,表示在引用的地方向后查找; 局部标号一般用数字1、2
点号“.”在汇编中表示当前指令的地址
直接操作数前缀用#或$
.globalxx(有时写为.globl)给符号xx外部链接属性,一般为了在别的文件中引用这个符号
.section.mysection自定义数据段,段名为“.mysection”
.align 2 4字节对齐
.balign/.balignw/.balignl字节/2字节/4字节填充
注意.align数字是2的n次方,而.balign数字是直接数字
.ascii "string..."定义一个ascii字符串
.byte .short .long .word .quad .float .string .ascii
.equ .set赋值语句
.end 文件结束
.include 头文件包含
.if .else .endif条件编译,类似于C语言的#if #elif #endif
.arm / .code32以ARM指令格式编译
.thumb / .code16以thumb指令格式编译
nop
ldr
adr
宏定义
.macro macroname
宏体
.endm
1.2.20.ARM汇编伪指令
伪指令的意义
伪指令不是指令,伪指令和指令的根本区别是经过编译后会不会生成机器码。
伪指令的意义在于指导编译过程。
伪指令是和具体的编译器相关的,我们使用gnu工具链,因此学习gnu环境下的汇编伪指令。
gnu汇编中的一些符号
@ 用来做注释。可以在行首也可以在代码后面同一行直接跟,和C语言中//类似
# 做注释,一般放在行首,表示这一行都是注释而不是代码。
:以冒号结尾的是标号
. 点号在gnu汇编中表示当前指令的地址
# 立即数前面要加#或$,表示这是个立即数
常用gnu伪指令
.global _start@ 给_start外部链接属性
.section .text@ 指定当前段为代码段
.ascii .byte .short .long .word
.quad .float .string @ 定义数据
.align 4@ 以16字节对齐
.balignl 16 0xabcdefgh @ 16字节对齐填充
.equ@ 类似于C中宏定义
偶尔会用到的gnu伪指令
.end@标识文件结束
.include@ 头文件包含
.arm / .code32@声明以下为arm指令
.thumb / .code16@声明以下为thubm指令
最重要的几个伪指令
ldr大范围的地址加载指令
adr小范围的地址加载指令
adrl中等范围的地址加载指令
nop空操作
ARM中有一个ldr指令,还有一个ldr伪指令
一般都使用ldr伪指令而不用ldr指令
adr与ldr
adr编译时会被1条sub或add指令替代,而ldr编译时会被一条mov指令替代或者文字池方式处理;
adr总是以PC为基准来表示地址,因此指令本身和运行地址有关,可以用来检测程序当前的运行地址在哪里
ldr加载的地址和链接时给定的地址有关,由链接脚本决定。