提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
1.8种寻址方式
寄存器寻址 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可以是其他任何字母或单词)
2.指令后缀
同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:
b(byte)功能不变,操作长度变为8位
h(half word)功能不变,长度变为16位
s(signed)功能不变,操作数变为有符号
如 ldr ldrb ldrh ldrsb ldrsh
s(S标志)功能不变,影响CPSR标志位
如 mov和movs movs r0, #0
3.条件执行后缀
比较有用的指令,比如beq 条件,当条件相等,则执行跳转。
4.数据传输和跳转指令详解
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
5.协处理器和协处理器指令详解
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>, , , , {<opcode_2>}
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
6.ldm/stm与栈的处理
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,一般用于从异常模式返回。
7.ARM汇编伪指令
GUN平台无关
(1)符号定义伪指令
.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
(2)数据定义伪指令
.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
(3)汇编控制伪操作
流程控制伪指令主要有.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 表示汇编结束
GUN平台相关
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
.balignl
字节对齐,保证此内容在以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
今天的文章ARM汇编指令集_arm汇编指令详解[通俗易懂]分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/67509.html