ARM汇编指令集3 --汇编伪指令
--参考朱有鹏裸机编程
1、协处理器和协处理器指令详解
什么是协处理器?协处理器的功能?
(1)MCR & MRC
MRC :用于读取CP15中的寄存器
MCR: 用于写入CP15中的寄存器
(2)什么是协处理器?
1、SOC内部另一个处理核心,协助主CPU实现某些功能,被主CPU调用执行一定的任务。
2、ARM设计上支持多达16个协处理器,但是一般的SOC只实现其中的CP15
3、协处理器和MMU、CACHE、TLB等处理有关,功能上和操作系统的虚拟地址映射,Cache管理等有关。
(3)怎么使用MCR & MRC
MRC & MCR的使用方法:
ARM Cp 15协处理器
使能MMU
Mrc(读) p15 , 0, r0, c1,c0 ,0
Mcr {<cond>} p15,<opcode_1> , <Rd>,
<Crn>, <Crm>,{<opcode_2>}
Rd: ARM 的普通的寄存器,不能是R15
Crn:CP15的寄存器,合法值是C0~C15
CRM:CP15的寄存器,一般设为C0
Opcode_2:0
PS:对于协处理器学习的要点,不必深究,将UBOOT中和Kernel中起始代码中的一般操作搞明白就可以了。
2、只看一般的用法,不详细区分参数的细节,否则会陷入很多复杂的未知
3、关键在于理解,而不是死记硬背
2、LDM、STM批量处理的指令
从UBOOT搬到别的地方,做一个重定位的时候。
LDR、str每一个周期只能访问4字节的内存,如果需要批量读取,写入
内存的速度太慢,解决方案是:
STM、LDM(load register mutiple)
并行处理的,是为了提高频繁的大量的数据读取和写入的时候。
Stmia sp,{r0 - r12}
将r0存入sp指向的内存处(假设为0x30001000)
然后地址+4(即指向0x30001004),将r1存入该地址,然后地址再+4(指向0x30001008)
直到r12内容放入(0x3001030)
在一个访问周期同时完成13个寄存器的读写。
(单周期多寄存器访问)
把SP理解为数组名
3、几种指令的后缀
ia: 先传输,再地址+4
ib: 先地址+4,再传输
da 先传输,再地址+4
db: 先地址-4,再传输
fd: 满栈的递减堆栈
ed: 空递减堆栈
Fa 满递减堆栈
ea 空递减堆栈
4/4种堆栈的形式
空栈:
栈指针指向空位,每次存入时可以直接存入然后栈指针移动一格,而取出的时候需要先移动一格才能取出。
(存储的地方是空的,先存后放)
满栈:
栈指针指向栈中最后一格数据,每次存入时需要先移动栈指针一格再存入;取出时可以直接取出,然后再移动栈指针。
(SP始终指向的是我们栈中满了的地方)
(SP+1,先指针的移动,再存储数据)
增栈:
栈指针移动时向地址增加的方向移动的栈
(可以往地址更大的地方移动)
减栈:
栈指针移动时向地址减少的方向移动的栈
(可以往地址更小的地方移动)
5、ARM汇编的部分伪指令
(1)指令经过编译之后会生成机器码,但是伪指令经过编译之后就消失了。伪指令意义在于指导编译过程。
伪指令是和具体的编译器有关的,我们嵌入式开发就是用GNU工具链,
因此学习GNU环境下的汇编的伪指令
(2)在GCC里面,C和汇编是可以互相通用的。
@就是汇编里面注释的,跟C语言中//类似
:以冒号结尾的是标号(标号代表是后面指令的地址)
.点号在GNU汇编中表示当前指令的地址
#立即数前面要加#或者$表示这个是立即数
(3)汇编写一个死循环:
while(1) C语言(for(;;))
flag:
b flag
常用的GNU伪指令:
.global _start @给_start外部链接属性,相当于在别的文件定义了,想要在别的文件中使用
.section .text @指定当前段为代码段,因为代码段是在运行的时候不变的,应该跟数据段分来
.ascii .byte .short .long .word :非常类似于我们C语言的数据类型,定义变量的
IRQ_STACK_START:
.word 0x0badc0de
等价于 unsigned int IRQ_STACK_START = 0x0badc0de
@定义数据
.align 4 @以16字节对齐
.align 2 以4字节对齐 2^2
.balignl 16 0xabcdefgh @16字节对齐填充
b表示位填充;align表示要对齐,l表示long,以4字节为单位对齐。
.equ @类似于C中的宏定义
最重要的几个伪指令:
Ldr :大范围的地址加载指令
Adr :小范围的地址加载指令
Adrl :中等范围的地址加载指令
Nop :空操作
ARM中有一个ldr指令(前面是一个#号),还有一个ldr伪指令()
Ldr指令: ldr r0,#0xff
伪指令:ldr r0, = 0xff
涉及到合法、非法立即数,涉及到ARM文字池
如果你用等号的话,就不用考虑后面的地址的长度。
如果是一个标号,所以不用=表示。
Adr与ldr
Adr编译时会被1条sub或者add指令替代,而ldr编译时会被一条mov指令替代或者文字池的方式处理
adr总是以PC为基准来表示地址,因此指令本身和运行地址有关,可以用来检测程序当前的运行地址在哪里
(这个地址就是一个相对的地址)
ldr加载的地址和链接时给定的地址有关,由链接脚本决定
Adr和ldr的差别:ldr加载的地址在链接时确定
而adr加载的地址在运行时决定
所以我们可以通过adr和ldr加载的地址,当前程序是否在链接时指定的地址运行。
跟地址的重定位是有关的