开始想的比较简单,在开始配置uboot的时候开始看最开始的汇编,发现了之前用arm7当单片机用的时候没有涉及到汇编这些东西,看到将arm指令相关基础知识的时候感觉没必要看,现在才发现这个是基本功,如果想进入嵌入式或者驱动开发,必须要了解芯片架构和指令系统,arm在arm7之后最主要的就是有了mmu功能,这个也是arm+linux的基本功。“基本功”,我师傅常说的一个词,现在真的领会到了它的含义。做东西还是不能只了解表面的东西,尤其是嵌入式驱动方面一方面和软件打交道,另外最重要的一点要理解硬件。这才是嵌入式软件和通用软件的最大区别。
下面开始学习下arm的寻址方式。这里主要是参考《arm嵌入式程序设计》这本小册子,讲的arm的基础知识,初学的同学们可以看看,和8086对比着讲的,很不错。
arm有一下的集中寻址方式:
1. 立即数寻址
这是一种比较简单的寻址方式,操作数包含在指令当中。取出指令,即取出了操作数。对应的寻址方式也就叫做立即数寻址。
例:
;注意arm汇编中,指令不能顶格写,在前面加空格。
;而定义变量的时候,必须顶格写,这里新手要注意。
ADD R0,R0,#1 ;R0 <==R0 +1 ,把R0的内容加1。
ADD R0,R0,#0x30 ;R0 <==R0 +0x30,把R0的内容加 0x30。
说明:以上两条指令中,第二个源操作数 #1 和#0x30为立即数。立即数必须以#作为前缀,对于16进制的立即数,要求有"0x"或者"&"符号。
2. 寄存器寻址
将寄存器中的值作为操作数,这种方式即为寄存器寻址。此种方式较为各类微处理器常用,执行效率高。
例:
ADD R0,R1,R3 ;R0 <==R1+R3
将R1和R3的内容相加,结果存放到R0。
3. 寄存器移位寻址
其是将2 寄存器寻址做变化而得到的一种新的寻址方式。即移位,第二个操作数通过相应移位和第一个操作数运算后放到目的寄存器中。
例:
ADD R0,R1,R3,LSL #3 ;R0 <== R0+R3*8 ,R3的值左移3位就是乘以8,结果与R1的值相加,存放到R0.
ARM的移位(或者循环移位)操作有如下几种:
LSL 逻辑左移,寄存器中低位空出的有效位用0填充
LSR 逻辑右移,寄存器中高端空出的有效位用0填充
ASR 算数右移,移位过程中必须保持符号位不变,即若源操作数为正,则高位用0填充,若为负,则高位用1填充。 其每移一位的操作和逻辑右移相同,仅是如果原最高位为1,则移位后最高位补1,所以在移大于1位时左边的高位都是1.
即:
MOV R1,#0
MOV R2,#&80000000 ;此处立即数为负数,
MOV R1,R2,ASR #3
R1 内的值为 0xF0000000
若立即数换为0x40000000,则R1的值最后为0x08000000.
ROR 循环右移,一处的最低位移到空出的最高位
RRX 扩展的循环右移,操作数右移一位(每次执行只能移动1位),高端空出的位用进位标志位C的当前值来填充。
4.寄存器间接寻址
寄存器间接寻址,就是以寄存器中的值作为操作数的地址。这种寻址方式类似于c语言中取指针对应的变量的值 *p。
例:
ADD R0,R1,[R2] ;R0 《==R1 + [R2]
LDR R0,[R1] ;R0 <==[R1]
STR R0,[R1] ;[R1] <== R0
第一条指令为将R2的值作为操作数的地址,将此地址对应的位置的操作数和R1相加,存放到R0 ,[R2]类似于 取指针对应的值 *p。
第二条指令为将R1的值作为地址的寄存器中的数传送到R0中。类似于c语言 R0 = *R1;
第三条指令将R0的值传送到R1的值作为地址的寄存器中。 类似于c语言中 *R1 = R0;
5.基址变址寻址
这种寻址方式就是将寄存器(一般为基址寄存器)的内容与指令中给出的地址偏移量相加,从而得到操作数的有效地址。
这种方式长用来访问基址附近的地址空间。
例:
LDR R0,[R1,#4] ;R0 <==[R1 +4]
LDR R0,[R1,#4]! ;R0<==[R1+4] 、 R1<==R1 +4
LDR R0,[R1],#4 ;R0<==[R1] 、R1 <==R1+4
LDR R0,[R1,R2] ;R0 <==[R1+R2]
以上是常见的4中变址寻址方式,通过前面4中寻址的学习,我们可以认为 符号"[]"意为里面的数是地址,用符号作用后是取地址对应的值。即 *(int *)强制类型转换,
#即代表的立即数。那么第一条和第四条就很容易理解了。
第二三条指令可以看出,相当于两个动作,第一个动作是将取得的值存放到R0,第二个都是R1的地址自加4.
6.多寄存器寻址
多寄存器寻址方式可以实现一条指令完成多个寄存器值的传送。最多可以传送16个通用寄存器的值。
例:
LDMIA R0,{R1,R2,R3,R4} ;R1 <==[R0]
;R2 <==[R0+4] ; R3 <==[R0+8] ;R4 <==[R0+12]
该条指令的后缀 IA 即 Increment After 的缩写,表示在每次执行完加载/存储操作之后,R0按字长度(即4字节)增加,因此,指令可以将连续多个存储单元的值传送到R1-R4中。
7.相对寻址
这种寻址方式从名称上就可以看出和基址变址寻址方式类似,相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号为偏移量,将两者相加之后得到操作数的有效地址。
以下程序完成了子程序的调用和返回,跳转指令BL采用相对寻址方式:
BL NEXT ;跳转到子程序NEXT处执行 ,采用BL或者BLX跳转时, LR(即寄存器R14)保存了返回地址
……
NEXT
……
MOV PC,LR ;从子程序返回,将返回地址存到PC(即寄存器R15)
8.堆栈寻址
堆栈(STACK)是一种十分常用的数据结构,按照先进后出(First In Last Out,FILO)的方式工作。堆栈使用一个叫做堆栈指针的专用寄存器指示当前操作位置,堆栈指针总是指向栈顶。ARM中使用LDMFD和STMFD指令来支持POP出栈操作和PUSH进栈操作,R13被用作堆栈指针。
例:
STMFD R13!,{R0~R4} ;
LDMFD R13!,{R0~R4} ;
第一条指令将R0~R4中的数据压入堆栈
第二条指令将数据出栈,回复R0~R4的值。