一.ARM裸机学习之ARM汇编
2017-10-26 22:40
239 查看
四.ARM汇编 2017/10/26 22:39
汇编指令:CPU指令的助记符
伪汇编指令:本质上不是指令,是编译器环境提供的,目的是用来指导编译过程,经过编译后伪指令最终不会生成机器码
ARM汇编的特点1:LDR/STR架构
ARM采用RISC架构,CPU本身不能直接读取内存(CISC结构的可以直接读取内存),而需要先将内存中内容载入CPU中通用寄存器才能被CPU执行。
lldr (load register) 将内存内容加载进入通用寄存器
str (store register) 将寄存器内容存入内存空间
ARM汇编的特点2:8种寻址方式
ARM汇编的特点3:指令后缀
同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:
ARM汇编的特点3:条件执行后缀
如果后缀条件满足才执行。
例如:
mov r0, r1 @r0 = r1
moveq r0,r1 @ if(eq) r0 =r1
条件后缀执行注意:条件后缀是否成立,不是取决于本句代码,而是取决于这句代码之前的代码运行后的结果。
ARM汇编的特点4:多级指令流水线
S5PV210使用13级流水线。PC指向正被取值的指令,而非正在执行的指令。
多级指令流水线与一级一条指令执行的过程比较说明
简单来说,执行某条指令至少要通过取指、译码、执行三个步骤。这就好像盲人在吃饭,第一步是用筷子夹出要吃的东西(从内存中取出指令),第二步是把吃的东西举到鼻子底下闻一下看看是否能吃(分析该指令),第三步是放到嘴里吃(执行指令)。即盲人最少要浪费3秒钟。如果CPU也采取同样的方法,执行一条指令,那就意味着
CPU需消耗3个指令周期才能完成一个动作,可见其运行效率的低下
为了弥补这个问题,ARM采用了一种多级流水线的指令执行方式,即是利用多个时钟周期的时间去执行多条指令,从而大大提高了代码运行效率。
这就好像一位乐于助人的科学家,知道了盲人吃饭的故事之后,给这位盲人制作了两只机械手,现在盲人已经有三只手了,那么他会怎样吃饭呢?当他的第一只手把吃的送到嘴里吃的时候(执行指令),第二只手已经将另外的食物凑到鼻子底下闻了(分析指令),而第三只手此时正在从盘子里夹第三样东西呢。从此,盲人吃饭的效率就提高了三倍。即提高了效率。
五.数据传输指令
mov (move)
mov r1, r0 @两个寄存器之间数据传递 r1 = r0mov r1, #0xFF @将立即数赋值给寄存器 r1 = 0xFF
mvn 用法和mov一样,区别是mvn是按位取反后传递
r1 = 0xFF,执行mov r0, r1 后 r0 = 0xFF
执行mvn r0, r1 后 r0 = 0xffffff00
算术指令
add 加法指令add r2, r0, r1 @(r2 = r0+r1)
sub 减法指令sub r2, r0, r1 @(r2 = r0 - r1)
rsb 逆向减法指令
adc 带进位加法指令
sbc 带借位减法指令
rsc 带借位的逆向减法指令
逻辑指令
and 逻辑与
orr 逻辑或
eor 逻辑异或
bic 位清除指令 (bic r0, r1, #0x1F @将r1中的数的bit0到bit4清零后赋值给r0 0x1F = 0001 1111)
比较指令
cmp cmp r0, r1 @(r0 - r1 = 0?)
cmn cmn r0, r1 @(r0 + r1 = 0?)
tst tst r0, 0xf @测试r0的bit0-bit3是否全为0
teq teq r0,r1, @P = r0 EOR r1
比较指令不用后加S 就能影响CPSR中的标志位。
乘法指令
mvl mla umull umlal smull smlal 都不常用,这里只作为知识点罗列。
前导零计数
clz 返回操作数二进制编码中第一个1前0的个数
CPSR访问指令
mrs & msr
mrs 用来读psr (cpsr&spsr)
msr 用来写psr
mrs r0, cpsr 将cpsr的值读入到r0中
……………… 处理r0的值
msr cpsr, r0 将r0的值写入到cpsr中
cpsr : 程序状态寄存器,CPU中只有一个,记录程序运行状态
spsr:CPU中有五个,分别在五种异常模式下,作用是从普通模式进入异常模式时,用来保存之前普通模式下的cpsr的,在返回普通模式时恢复原来的cpsr。
跳转指令
b & bl &bx
b 直接跳转(没打算返回)
bl (branch and link) 跳转前把返回地址存在lr寄存器中,以便返回。
bx 跳转同时切换到ARM模式,一般用于异常处理的跳转。
访存指令
ldr/srt & ldm/stm & swp
单个字/半字/字节访问 ldr/str
多字批量访问 ldm/stm
swp r1, r2, [r0] 内存和寄存器交换指令,将r0所指向内存中的数据写入r1,并将r2中的数据写入到r0所指向的内存。
swp r1, r1, [r0] 互换
软中断指令
swi (software interrupt) 用来实现操作系统中的系统调用。
汇编中的立即数
ARM指令都是32位,除了指令标记和操作标记外,本身只能附带很少位数的立即数,因此立即数有合法和非法之分。
合法立即数:经过任意位数的移位后非零部分可以用8位标识的即为合法立即数(非零部分少于等于8位,0x000000ff 是合法立即数,0x00ff0000是合法立即数,0xf000000f循环移位之后仍然是合法立即数,0x000001ff是非法立即数)
六.协处理器以及协处理器指令
什么是协处理器
Soc内部另一处理核心,协助CPU实现某些功能,被主CPU调用执行一定的任务。CP15 (cooperation processor)
协处理器和MMU、cache、TLB等处理有关,功能上和操作系统的虚拟地址映射、cache等的管理有关。
协处理器访问指令
mcr & mrc
mrc 用于读取CP15中的寄存器
mcr 用于写CP15中的寄存器
Rd:ARM的普通寄存器,不能是r15/pc
Crn:cp15的寄存器,合法值为c0 - c15
Crm:cp15的寄存器,一般均为c0
mrc p15, 0, r0, c1, c0, 0
mcr p15, 0, r0, c1, c0, 0
ldm/stm与栈的处理
ldr/str每周期只能访问4个字节内存,如果需要批量读取,写入内存时太慢,这个时候就要用ldm/stm (load register mutiplt / store register mutiplt)
多寄存器访问举例
stmia sp, {r0-r12}
将r0存入sp指向的内存处,然后地址+4,将r1存入内存,然后地址再+4……直到将r12内容存入内存。
1
2
栈类型
空栈:栈指针指向空位,每次存入数据后,sp+4
满栈:栈指针指向满位,每次存入数据时,先sp+4然后再存入数据
增栈:栈指针移动时向地址增加的方向移动
减栈:栈指针移动时向地址减小的方向移动
因此有四种栈类型:空增栈,空减栈,满增栈,满减栈。
八种后缀
stmia和stmfd用的最多,其他基本没用过
操作栈的时候使用相同的后缀,就会避免出错。
!的作用
ldmia r0, {r2-r3} @把r0指向的内存中的数据读入到r2中,然后内存地址+4再将+4后地址中的数据读入到r3中。指令执行完毕后r0中的值不变。
ldmia r0!, {r2-r3} @把r0指向的内存中的数据读入到r2中,然后内存地址+4再将+4后地址中的数据读入到r3中。指令执行完毕后r0中的值变化。
感叹号的作用就是r0的值在ldm过程中发生的增加或者减小最后写会到r0中。
^的作用
ldmfd sp!, {r0-r6, pc}
ldmfd sp!, {r0-r6, pc}^
^:在目标寄存器中有pc时,会同时将spsr写入到cpsr,一般用在异常返回的时候。
七.GNU汇编伪指令
伪指令和指令最大的区别是经过编译后不会生成机器码
伪指令是和具体的编译器相关的,我们使用gnu工具链,因此学习gnu环境下的汇编伪指令。
GNU汇编中的一些符号
@ 行注释,可以在指令后边也可以在行首
: 以冒号结束的是标号
. 点号表示当前指令的地址
立即数前面加#或者$,表示这是个立即数
常见的gnu汇编伪指令
AAAA:.word 0xAABBFF 类似于C语言的 int AAAA = 0xAABBFF;
.balignl 16, 0xABCDEF 对齐+填充,b表示位填充,最后的l表示long,16表示16字节对齐,0xABCDEF表示填充的原料。
0x00000008 : .balignl 16, 0xABCDEF,
0x0000000C: 0xABCDEF
0x00000010: 下一条指令
ARM中有一个ldr指令,还有一个ldr伪指令。两者的区别:
ldr r0, #0xFF @ldr指令
ldr r0, =0xFF @ldr伪指令 涉及到合法/非法立即数,还涉及到ARM文字池
adr 和 ldr的差别:
- adr编译时会被1条sub或add指令替代,而ldr编译时会被一条mov指令替代或者文字池方式处理;
- adr总是以PC为基准来表示地址,因此指令本身和运行地址有关,可以用来检测程序当前的运行地址在哪里
-ldr加载的地址和链接时给定的地址有关,由链接脚本决定。
汇编指令:CPU指令的助记符
伪汇编指令:本质上不是指令,是编译器环境提供的,目的是用来指导编译过程,经过编译后伪指令最终不会生成机器码
ARM汇编的特点1:LDR/STR架构
ARM采用RISC架构,CPU本身不能直接读取内存(CISC结构的可以直接读取内存),而需要先将内存中内容载入CPU中通用寄存器才能被CPU执行。
lldr (load register) 将内存内容加载进入通用寄存器
str (store register) 将寄存器内容存入内存空间
ARM汇编的特点2:8种寻址方式
类别 | 示例 |
寄存器寻址 | mov r1, r2 |
立即寻址 | mov r0, #0xFF00} |
寄存器移位寻址 | mov r0, r1, lsl #3 @(r0=r1<<3) |
寄存器间接寻址 | ldr r1, [r2] |
基址变址寻址 | ldr r1,[r2,#4] @r1 = r2+4 |
多寄存器寻址 | ldmia r1!,{r2-r7,r12} |
堆栈寻址 | stmfd sp!, {r1-r7,lr} |
相对寻址 | beq flag |
同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:
后缀 | 功能 |
B (byte) | 功能不变,操作长度变为8位 |
H (half word) | 功能不变,长度变为16位 |
S (signed) | 功能不变,操作数变为有符号 如 ldr ldrb ldrh ldrsb ldrsh |
S (S标志) | 功能不变,影响CPSR标志位 如 mov和movs |
如果后缀条件满足才执行。
例如:
mov r0, r1 @r0 = r1
moveq r0,r1 @ if(eq) r0 =r1
条件后缀执行注意:条件后缀是否成立,不是取决于本句代码,而是取决于这句代码之前的代码运行后的结果。
ARM汇编的特点4:多级指令流水线
S5PV210使用13级流水线。PC指向正被取值的指令,而非正在执行的指令。
多级指令流水线与一级一条指令执行的过程比较说明
简单来说,执行某条指令至少要通过取指、译码、执行三个步骤。这就好像盲人在吃饭,第一步是用筷子夹出要吃的东西(从内存中取出指令),第二步是把吃的东西举到鼻子底下闻一下看看是否能吃(分析该指令),第三步是放到嘴里吃(执行指令)。即盲人最少要浪费3秒钟。如果CPU也采取同样的方法,执行一条指令,那就意味着
CPU需消耗3个指令周期才能完成一个动作,可见其运行效率的低下
为了弥补这个问题,ARM采用了一种多级流水线的指令执行方式,即是利用多个时钟周期的时间去执行多条指令,从而大大提高了代码运行效率。
这就好像一位乐于助人的科学家,知道了盲人吃饭的故事之后,给这位盲人制作了两只机械手,现在盲人已经有三只手了,那么他会怎样吃饭呢?当他的第一只手把吃的送到嘴里吃的时候(执行指令),第二只手已经将另外的食物凑到鼻子底下闻了(分析指令),而第三只手此时正在从盘子里夹第三样东西呢。从此,盲人吃饭的效率就提高了三倍。即提高了效率。
五.数据传输指令
mov (move)
mov r1, r0 @两个寄存器之间数据传递 r1 = r0mov r1, #0xFF @将立即数赋值给寄存器 r1 = 0xFF
mvn 用法和mov一样,区别是mvn是按位取反后传递
r1 = 0xFF,执行mov r0, r1 后 r0 = 0xFF
执行mvn r0, r1 后 r0 = 0xffffff00
算术指令
add 加法指令add r2, r0, r1 @(r2 = r0+r1)
sub 减法指令sub r2, r0, r1 @(r2 = r0 - r1)
rsb 逆向减法指令
adc 带进位加法指令
sbc 带借位减法指令
rsc 带借位的逆向减法指令
逻辑指令
and 逻辑与
orr 逻辑或
eor 逻辑异或
bic 位清除指令 (bic r0, r1, #0x1F @将r1中的数的bit0到bit4清零后赋值给r0 0x1F = 0001 1111)
比较指令
cmp cmp r0, r1 @(r0 - r1 = 0?)
cmn cmn r0, r1 @(r0 + r1 = 0?)
tst tst r0, 0xf @测试r0的bit0-bit3是否全为0
teq teq r0,r1, @P = r0 EOR r1
比较指令不用后加S 就能影响CPSR中的标志位。
乘法指令
mvl mla umull umlal smull smlal 都不常用,这里只作为知识点罗列。
前导零计数
clz 返回操作数二进制编码中第一个1前0的个数
CPSR访问指令
mrs & msr
mrs 用来读psr (cpsr&spsr)
msr 用来写psr
mrs r0, cpsr 将cpsr的值读入到r0中
……………… 处理r0的值
msr cpsr, r0 将r0的值写入到cpsr中
cpsr : 程序状态寄存器,CPU中只有一个,记录程序运行状态
spsr:CPU中有五个,分别在五种异常模式下,作用是从普通模式进入异常模式时,用来保存之前普通模式下的cpsr的,在返回普通模式时恢复原来的cpsr。
跳转指令
b & bl &bx
b 直接跳转(没打算返回)
bl (branch and link) 跳转前把返回地址存在lr寄存器中,以便返回。
bx 跳转同时切换到ARM模式,一般用于异常处理的跳转。
访存指令
ldr/srt & ldm/stm & swp
单个字/半字/字节访问 ldr/str
多字批量访问 ldm/stm
swp r1, r2, [r0] 内存和寄存器交换指令,将r0所指向内存中的数据写入r1,并将r2中的数据写入到r0所指向的内存。
swp r1, r1, [r0] 互换
软中断指令
swi (software interrupt) 用来实现操作系统中的系统调用。
汇编中的立即数
ARM指令都是32位,除了指令标记和操作标记外,本身只能附带很少位数的立即数,因此立即数有合法和非法之分。
合法立即数:经过任意位数的移位后非零部分可以用8位标识的即为合法立即数(非零部分少于等于8位,0x000000ff 是合法立即数,0x00ff0000是合法立即数,0xf000000f循环移位之后仍然是合法立即数,0x000001ff是非法立即数)
六.协处理器以及协处理器指令
什么是协处理器
Soc内部另一处理核心,协助CPU实现某些功能,被主CPU调用执行一定的任务。CP15 (cooperation processor)
协处理器和MMU、cache、TLB等处理有关,功能上和操作系统的虚拟地址映射、cache等的管理有关。
协处理器访问指令
mcr & mrc
mrc 用于读取CP15中的寄存器
mcr 用于写CP15中的寄存器
Rd:ARM的普通寄存器,不能是r15/pc
Crn:cp15的寄存器,合法值为c0 - c15
Crm:cp15的寄存器,一般均为c0
mrc p15, 0, r0, c1, c0, 0
mcr p15, 0, r0, c1, c0, 0
ldm/stm与栈的处理
ldr/str每周期只能访问4个字节内存,如果需要批量读取,写入内存时太慢,这个时候就要用ldm/stm (load register mutiplt / store register mutiplt)
多寄存器访问举例
stmia sp, {r0-r12}
将r0存入sp指向的内存处,然后地址+4,将r1存入内存,然后地址再+4……直到将r12内容存入内存。
1
2
栈类型
空栈:栈指针指向空位,每次存入数据后,sp+4
满栈:栈指针指向满位,每次存入数据时,先sp+4然后再存入数据
增栈:栈指针移动时向地址增加的方向移动
减栈:栈指针移动时向地址减小的方向移动
因此有四种栈类型:空增栈,空减栈,满增栈,满减栈。
八种后缀
stmia和stmfd用的最多,其他基本没用过
后缀 | 意义 |
ia (increase after) | 先传输,再地址+4 |
ib (increase before) | 先地址+4,再传输 |
da (decrease after) | 先传输,再地址-4 |
db (decreade before) | 先地址-4,再传输 |
fd (full decrease) | 满递减栈 |
ed (empty decrease) | 空递减栈 |
fa | 满递增栈 |
ea | 空递增栈 |
!的作用
ldmia r0, {r2-r3} @把r0指向的内存中的数据读入到r2中,然后内存地址+4再将+4后地址中的数据读入到r3中。指令执行完毕后r0中的值不变。
ldmia r0!, {r2-r3} @把r0指向的内存中的数据读入到r2中,然后内存地址+4再将+4后地址中的数据读入到r3中。指令执行完毕后r0中的值变化。
感叹号的作用就是r0的值在ldm过程中发生的增加或者减小最后写会到r0中。
^的作用
ldmfd sp!, {r0-r6, pc}
ldmfd sp!, {r0-r6, pc}^
^:在目标寄存器中有pc时,会同时将spsr写入到cpsr,一般用在异常返回的时候。
七.GNU汇编伪指令
伪指令和指令最大的区别是经过编译后不会生成机器码
伪指令是和具体的编译器相关的,我们使用gnu工具链,因此学习gnu环境下的汇编伪指令。
GNU汇编中的一些符号
@ 行注释,可以在指令后边也可以在行首
: 以冒号结束的是标号
. 点号表示当前指令的地址
立即数前面加#或者$,表示这是个立即数
常见的gnu汇编伪指令
伪指令 | 意义 |
.globl _start | 给_start外部链接属性 |
.section .text | 指定当前为代码段 |
.ascii .byte .short .long .word .quad .float .string | 定义数据 |
.align 4 | 以16(2^4)字节内存地址对齐 |
.balignl 16,0xABCDEF | 16字节对齐填充 |
.equ | 类似于C中的宏定义 |
.end | 汇编文件的结束,不加无所谓 |
.include | 头文件包含 |
.arm或者.code32 | 声明以下的代码为arm指令不是thumb指令 |
.thumb或者.code16 | 声明以下的代码为thumb指令 |
ldr | 大范围的地址加载指令 |
adr | 小范围的地址加载指令 |
adrl | 中等范围的地址加载指令 |
nop | 空操作 |
.balignl 16, 0xABCDEF 对齐+填充,b表示位填充,最后的l表示long,16表示16字节对齐,0xABCDEF表示填充的原料。
0x00000008 : .balignl 16, 0xABCDEF,
0x0000000C: 0xABCDEF
0x00000010: 下一条指令
ARM中有一个ldr指令,还有一个ldr伪指令。两者的区别:
ldr r0, #0xFF @ldr指令
ldr r0, =0xFF @ldr伪指令 涉及到合法/非法立即数,还涉及到ARM文字池
adr 和 ldr的差别:
- adr编译时会被1条sub或add指令替代,而ldr编译时会被一条mov指令替代或者文字池方式处理;
- adr总是以PC为基准来表示地址,因此指令本身和运行地址有关,可以用来检测程序当前的运行地址在哪里
-ldr加载的地址和链接时给定的地址有关,由链接脚本决定。
相关文章推荐
- 朱老师ARM裸机学习笔记(六):ARM汇编
- ARM裸机基础学习--汇编语法和指令
- ARM 裸机程序学习 03 - 发送SOS信号(汇编 + C)
- ARM汇编写的流水灯,完全裸机(对于汇编学习,板子启动初始化认识帮助很大)
- 朱老师ARM裸机学习笔记(七):汇编写启动代码之调用C语言
- 浅析arm汇编中指令使用学习
- ARM汇编学习笔记
- ARM汇编学习经验谈
- 在一台ubuntu的arm板子上学习arm汇编
- ARM汇编程序学习
- GNU ARM汇编(综合的学习资料)
- arm裸机代码的学习
- TX2440 项目实战-2440init.s 祥解 (ADS1.2编译) arm汇编基础学习终结
- ARM学习笔记11——GNU ARM汇编程序设计
- ARM裸机程序开发7(汇编与c混合编程)
- ARM学习笔记6-裸机之GPIO-LED
- 汇编指令的学习1——ARM汇编的特点
- ARM学习笔记015之GPIO(汇编、key、led接线、int main、-wall)
- ARM裸机全集之ARM体系结构(学习笔记)
- ARM汇编学习笔记