ARM编程进阶之一-ARM汇编伪指令
ARM汇编伪指令共4条:ldr、adr、adrl、nop
1、ldr
首先我们来回答“基本寻址模式与基本指令”一文中提出的问题。“如果我们需要mov r0, #10000这样的指令,应该怎么办?(常数10000不能在机器指令32bit中的低12bit中被表示出来)”。当你进行编译的时候,“Error:All70E”的错误就会出现,如下图。
其实,这个问题很容易解决,只需要将mov r0, #10000换为ldr r0, =10000即可。为什么这样就可以了呢?因为,这里的ldr r0, =10000并非我们已经学过的ldr指令,而是一条伪指令,编译器会将这条伪指令替换为:
ldr r0, [pc, #-4]
DCD 10000
DCD所分配的内存空间中存放了整数10000,该内存空间被称为literal pool,中文名称“文字池”。由于整个程序都是由编译器编译的(包括文字池的分配),所以很显然编译器能够知道ldr指令在内存中的地址与文字池在内存中的位置之间的偏移量,因此编译器就可以正确地使用以pc为基址,采用相对寻址的ldr指令将文字池中的数取出加载到寄存器r0中。由此可见,编译器对于ldr r0, =10000这条伪指令的处理,其实质是:
在汇编源程序时,LDR伪指令被编译器替换成一条合适的指令和存放常数的文字池。汇编器将常量放入文字池,并使用一条程序相对偏移的LDR指令从文字池读出常量。
由于,4byte可以存放任何int型整数,这样一来,常数就可以是任何int型整数,而不再受制于12bit的限制。当然此时的常数是存放在内存中的,而不是存放在机器指令的32bit编码中的。
额外说明:
a)、ldr r0, [pc, #-4]中是-4,而不是+4,是由于流水线的原因(参见“流水线对PC值的影响”一文)。今后对于流水线的这种影响,我将不再予以特别说明。
b)、从指令位置到文字池的偏移量必须小于4KB
c)、从语法上来看,与ARM指令的LDR相比,伪指令LDR的参数有“=”号,没有“#”号
d)、如果常数能够被12bit表示出来,例如:ldr r0, =0x100,那么,编译器对该伪指令的处理,是使用MOV(或者MVN)指令代替该LDR伪指令,例如:mov r0, #0x100,而不会采用ldr指令+文字池的方式。
除了 ldr 寄存器, =常数 这种形式外,还有ldr 寄存器, =标号 这种形式也经常被使用,下面我就来讲解这种形式的ldr伪指令。
由上图可见:ldr pc, =InitStack这条伪指令的作用是将标号InitStack所代表的地址赋予pc。 这里会使我们产生几个疑问:
a)、为什么不使用bl InitStack,而要使用ldr pc, =InitStack?
这是因为bl指令的跳转范围是正负32M,而InitStack所代表的位置有可能距离ldr pc, =InitStack超过32M,此时bl就无能为力了。竟然存在这么大范围跳转的程序(这似乎意味着编译出来的二进制可执行文件的大小会操作32M),这一点似乎令我们感到非常震惊。事实上是:大小超过32M的可执行程序的确几乎不可能出现,但即使是很小的二进制程序中也可能进行大范围(超过32M)的跳转,这一点在bootloader程序中几乎是必然的。
b)、编译器是如何得出InitStack所代表的地址是0x64?
编译器知道MOV R0, LR这条指令相对于整个程序的第1条指令的偏移量为0x64;同时又知道这个程序将来在内存中的运行地址为0x0,所以编译器在编译的时候(不是程序运行的时候)就可以确定InitStack所代表的地址为0x64+0x0=0x64。那么编译器又是如何知道“程序将来在内存中的运行地址”的呢?其实,这个“程序将来在内存中的运行地址”,我通常称它为“程序的期望运行地址”,简称“运行地址”,以后我也将这样称呼它。它其实是在编译程序前由程序员告知编译器的。
c)、如果将来程序并没有运行在它的运行地址处,很显然这个程序就会出问题。如何解决?
出问题的原因,显然是由于ldr伪指令使用的绝对地址。所以,解决的办法就是使用相对地址,进行相对寻址。这就要用到我们下面要介绍的另一条伪指令adr
2、adr
ADR伪指令的作用与LDR伪指令的作用相同,都是将标号所代表
ARM编程进阶汇编伪指 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)