提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
寄存器寻址 mov r1, r2(C语言中的r1=r2)
立即寻址(立即数) mov r0, #0xFF00(加#表示是一个数字)
寄存器移位寻址 mov r0, r1, lsl #3(r1左移3位生成的数赋给r0,即乘2^3=8)
寄存器间接寻址 ldr r1, [r2]([r2]代表内存,内存的地址就在r2里面存着,r2就相当于指针,将[r2]中的内容给r1)
基址变址寻址 ldr r1, [r2, #4](r2+4)
多寄存器寻址 ldmia r1!, {r2-r7, r12}(加载:从内存往寄存器加载。一次访问7个寄存器r2到r7,r12,r1内存着内存的地址,并且r1相当于一个数组,r1是数组的数组名,也就是数组的头地址,里面有7个元素)
堆栈寻址 stmfd sp!, {r2-r7, lr}(从栈指针里面连续访问(压栈,弹栈))
相对寻址 beq flag(以pc为参考作一个偏移量)
flag:(是作为标号的,上面的指令执行时将跳到这个标号所在处执行,flag可以是其他任何字母或单词)
同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:
b(byte)功能不变,操作长度变为8位
h(half word)功能不变,长度变为16位
s(signed)功能不变,操作数变为有符号
如 ldr ldrb ldrh ldrsb ldrsh
s(S标志)功能不变,影响CPSR标志位
如 mov和movs movs r0, #0
1.数据处理指令:
数据传输指令 mov mvn
算术指令 add sub rsb adc sbc rsc
逻辑指令 and orr eor bic(位清除指令)
Bic r0,r0,#0x1f 将r0中的bit0到bit4清零后赋值给r0
比较指令 cmp cmn tst teq
(比较指令不用加后缀就可以影响cpsr中的标志位)
乘法指令 mvl mla umull umlal smull smlal
前导零计数 clz
2. cpsr访问指令
mrs用来读psr,msr用来写psr
CPSR寄存器比较特殊,需要专门的指令访问,这就是mrs和msr。
程序举例:@mrs r0,cpsr
@bic r0,r0,#0x1f
@orr r0,r0,#0xd3
@msr cpsr,r0
msr cpsr_c,#0xd3 (_c是cpsr的一个块,上文有图可看出cpsr分为f,s,x,c四块,所以这一条语句=上面四条语句)
3.跳转(分支)指令
b & bl & bx
b:直接跳转(就没打算返回,相当于C语言的goto指令)
bl:branch and link,跳转前把返回地址放入lr中,以便返回,用于函数调用
bx:跳转同时切换到ARM模式,一般用于异常处理的跳转。(现在没用了)
4.访存指令
ldr/str & ldm/stm & swp
单个字/半字/字节访问 ldr/str(若加上指令后缀,如h则访问半个字的内容)
多字批量访问 ldm/stm
swp r1, r2, [r0](此指令是一边读一边写的,将r0读r1,将r2读到r0)
swp r1, r1, [r0](寄存器和内存交换内容)
5.立即数
合法立即数与非法立即数。
ARM指令都是32位,除了指令标记和操作标记外,本身只能附带很少位数的立即数。因此立即数有合法和非法之分。
合法立即数:经过任意位数的移位后非零部分可以用8位表示的即为合法立即数。合法:0xff,0xf00000f。非法:x01ff
6.软中断指令
swi(software interrupt)
软中断指令用来实现操作系统中系统调用
7.指令举例:
1.mov(move) mov r1, r0 @两个寄存器之间数据传递
mov r1, #0xff @将立即数赋值给寄存器
mvn和mov用法一样,区别是mov是原封不动的传递,而mvn是按位取反后传递。按位取反的含义:譬如r1 = 0x000000ff,然后mov r0, r1 后,r0 = 0xff但是我mvn r0, r1后,r0=0xffffff00。
2.and 逻辑与
orr 逻辑或
eor 裸机异或
3.bic 位清除指令
bic r0,r1,#0x1f @ 将r1中的数的bit0到bit4清零后赋值给r0 0x1f = 0x0000001f=0x0000```11111
4.比较指令:
cmp cmp r0, r1 等价于 sub r2, r0, r1 (r2 = r0 - r1)
cmn cmn r0, r1 等价于 add r0, r1
tst tst r0, #0xf @测试r0的bit0~bit3是否全为0
teq
比较指令用来比较2个寄存器中的数
注意:比较指令不用后加s后缀就可以影响cpsr中的标志位。
5.合法立即数: 0x000000ff 0x00ff0000 0xf000000f
非法立即数: 0x000001ff
1.协处理器cp15操作指令
mrc用于读取CP15中的寄存器
mcr用于写入CP15中的寄存器
2.什么是协处理器
SoC内部另一处理核心,协助主CPU实现某些功能被主CPU调用执行一定任务。
ARM设计上支持多达16个协处理器,但是一般SoC只实现其中的CP15.(cp:coprocessor)。
协处理器和MMU、cache、TLB等处理有关,功能上和操作系统的虚拟地址映射、cache管理等有关。
3.MRC & MCR的使用方法
mcr{} p15,
opcode_1:对于cp15永远为0
Rd:ARM的普通寄存器,不能是r15(pc),否则结果不定,
Crn:cp15的寄存器,合法值是c0~c15(都是特殊功能寄存器,cp15的寄存器详情查百度)
Crm:cp15的寄存器,一般均设为c0
opcode_2:一般省略或为0
举例(来自于uboot)
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #1
mcr p15, 0, r0, c1, c0, 0
1.为什么需要多寄存器访问指令
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个寄存器的读写
3.8种后缀
ia(increase after)先传输,再地址+4
ib(increase before)先地址+4,再传输
da(decrease after)先传输,再地址-4
db(decrease before)先地址-4,再传输
fd(full decrease)满递减堆栈
ed(empty decrease)空递减堆栈
fa(·······) 满递增堆栈
ea(·······)空递增堆栈
4.四种栈
空栈:栈指针指向空位,每次存入时可以直接存入然后栈指针移动一格;而取出时需要先移动一格才能取出
满栈:栈指针指向栈中最后一格数据,每次存入时需要先移动栈指针一格再存入;取出时可以直接取出,然后再移动栈指针
增栈:栈指针移动时向地址增加的方向移动的栈
减栈:栈指针移动时向地址减小的方向移动的栈
5.!的作用
ldmia r0, {r2 - r3}
ldmia r0!, {r2 - r3}
ia:increase after后缀,将r0对应的地址里的内容读到r2,再地址加4,将加4后的地址里的内容读到r3,注意是地址中的内容,不是地址值。读好后r0中的值不变,还是初始地址值。
感叹号的作用就是r0的值在ldm过程中发生的增加或者减少最后写回到r0去,也就是说ldm时会改变r0的值。
6.^的作用
ldmfd sp!, {r0 - r6, pc}
ldmfd sp!, {r0 - r6, pc}^
^的作用:在目标寄存器中有pc时,会同时将spsr写入到cpsr,一般用于从异常模式返回。
.global,.local,.set,.equ
.global
使得符号对连接器可见,变为对整个工程可用的全局变量,通俗讲就是定义全局变量
eg:
.global symbol
.local
表示符号对外部不可见,只对本文件可见,通俗讲就是定义局部变量
eg:
.local symbol
.set
给一个全局变量或局部变量赋值,和.equ的功能一样
eg:
.set symbol expr
.set start, 0x40
.set start, 0x50
mov r1, #start ;r1里面是0x50
.equ
和.set一样,只是格式不同
eg:
symbol .equ expr
start .equ, 0x40
start .equ, 0x50
mov r1, #start ;r1里面是0x50
.byte,.short,.long,.quad,.float,.string,.asciz,.ascii,.rept
.byte
在存储器中分配1个字节,用指定的数据对存储单元进行初始化
label: .byte expr ;label是程序标号,expr可以是-128~255的数字,也可是字符
eg:
a: .byte #1 ;等价于C语言中的char a=1;
.short
在存储器中分配2个字节,用指定的数据对存储单元进行初始化
eg:
a: .short 0x1234
.long / .word
在存储器中分配4个字节,用指定的数据对存储单元进行初始化
eg:
a: .word 0x12345678
.quad
在存储器中分配8个字节,用指定的数据对存储单元进行初始化
eg:
a: .quad 0x12345678 ;等价于C中的long a=0x1234567812345678
.float
在存储器中分配4个字节,用指定的浮点数据对存储单元进行初始化
eg:
a: .float 1.11
.space /.skip
用于分配一块连续的存储区域并初始化为指定的值,如果后面的填充值省略不写则在后面填充为0;
label: .space size,expr ;expr可以是4字节(32bit)以内的浮点数
eg:
a: space 8, 0x1
.string
定义一个字符串,默认是string8,还有string16,string32,string64
eg:
a: .space “hello world!”
.rpet
重复执行接下来的指令,以.rept开始,以.endr结束
eg:
.rept cnt ;cnt是重复次数
…
.endr
流程控制伪指令主要有.if .else .endif .macro .endm .exitm
.if, .else, .endif
.if logical-expression
…
.elseif logical-expression2
…
.else
…
.endif
.macro, .endm, .exitm
该伪指令可以将一段代码定义为一个整体,称为宏指令。然后就可以在程序中通过宏指令多次调用该段代码;而.exitm用于退出当前宏指令,宏指令可以使用一个或多个参数,当宏操作被展开时,这些参数被相应的值替换。包含在.macro和.endm之间的指令序列称为宏定义体。在宏定义体的第一行应该声明宏的原型,包含宏名所需要的参数,然后就可以在汇编程序中通过宏名来调用该指令序列。在源程序被编译时,编译器将宏调用展开,用宏定义中的指令序列代替程序中的宏调用,并将实际参数值传递给宏定义中的形式参数。
eg:
.macro macroname macargs …
…code…
.endm
杂项
.align 用于使程序当前位置满足一定的对齐方式
.section 用来定义一个段的伪指令
.data 用来定义一个数据段
.text 用来定义一个代码段
.include 用来包含一个头文件
.arm 定义以下代码使用arm指令集编译
.code 32 同.arm
.code 16 同.thumb
.thumb 定义以下代码使用thumb指令集编译
.extern 用于声明一个外部符号,用于兼容性其他汇编
.weak 用于声明一个弱符号,如果这个符号没有定义,编译就忽略,而不会报错
.end 表示汇编结束
ADR
把标签所在的地址加载到寄存器中,这个指令将基于PC相对偏移的地址或基于寄存器相对偏移的地址值读取到寄存器中。当地址是字节对齐时,取值范围为-255-255B;当地址是字对齐时取值范围是-1020-1020B。该指令相当于 add , pc, offset
eg:
ADR
ADRL
用于将中等范围地址读取到寄存器中
ADRL
LDR
装在一个32位的常数和一个地址寄存器,即将一个32位常数加载到一个寄存器中
ldr reg, =expr
reg:目标寄存器
expr:32位常量
汇编器根据expr的取值情况对ldr伪指令会做一下处理:
当expr表示的指令地址值没有超过MOV指令或MVN指令的地址取值范围时,汇编器用一对MOV和MVN代替LDR指令
当超过了的时候,汇编器将常数放入缓存,同时用一条基于PC的LDR读取该常数
eg:
LDR R3,=0xff0 @将常数0xff0读到内存中相当于MOV R3, #0xff0
LDR R1, =0xfff @将常数0xfff读到内存,相当于LDR R1,[pc, offset_to_litpool] … litpool DCD 0xfff
LDR R2, =place @将place标号的地址读入到R1中,相当于LDR R1,[pc, offset_to_litpool] … litpool DCD place
字节对齐,保证此内容在以8字节的整数倍的后面。
l表示long,8字节
w表示半字,也就是两字节,所以.balignw的意思也就一目了然
2.gnu汇编中的一些符号
@ 用来做注释。可以在行首也可以在代码后面同一行直接跟,和C语言中//类似# 做注释(存疑)一般放在行首,表示这一行都是注释而不是代码。
:以冒号结尾的是标号(标记这一行指令的地址,可以当作flag,用b来跳转)
. 点号在gnu汇编中表示当前指令的地址
C语言中死循环:while(1);
汇编死循环:flag:
b flag
或者 b.# 立即数前面要加#或$,表示这是个立即数
3.常用gnu伪指令
.global _start @ 给_start外部链接属性
.section .text @ 指定当前段为代码段
.ascii .byte .short .long .word @定义变量类型,相当于C中的int,char之类
.quad .float .string @ 定义数据
.align 4 @ 以16字节对齐:2^4
.balignl 16 0xabcdefgh @ 16字节对齐填充
4.偶尔会用到的gnu伪指令
.end @标识文件结束
.include @ 头文件包含
.arm / .code32 @声明以下为arm指令
.thumb / .code16 @声明以下为thubm指令
5.最重要的几个伪指令
ldr 大范围的地址加载指令
adr 小范围的地址加载指令
adrl 中等范围的地址加载指令
nop 空操作
ARM中有一个ldr指令,还有一个ldr伪指令
一般都使用ldr伪指令而不用ldr指令(不用考虑立即数是否合法)
Ldr#后面跟指令,ldr=后面跟伪指令。
6.adr与ldr
adr编译时会被1条sub或add指令替代,而ldr编译时会被一条mov指令替代或者文字池方式处理;
adr总是以PC为基准来表示地址,因此指令本身和运行地址有关,可以用来检测程序当前的运行地址在哪里
ldr加载的地址和链接时给定的地址有关,由链接脚本决定。
1、8种寻址方式
寄存器寻址
立即寻址
寄存器移位寻址
寄存器间接寻址
基址寻址
多寄存器寻址
堆栈寻址
相对寻址
2、6类指令:
数据处理指令:
数据传输指令, MOV MVN
算术指令, ADD SUB RSB ADC SBC RSC
逻辑指令, AND ORR EOR BIC
比较指令, CMP CMN TST TEQ
乘法指令, MUL MLA UMULL UMLAL SMULL SMLAL
前导零计数。
程序状态寄存器访问指令: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
点号“.”在汇编中表示当前指令的地址
直接操作数前缀用#或$
.global xx(有时写为.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