新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > ARM编程进阶之一-ARM汇编伪指令

ARM编程进阶之一-ARM汇编伪指令

作者: 时间:2016-11-27 来源:网络 收藏
到目前为止,我们已经具备编写较为复杂的ARM汇编程序的能力,但要编写较为复杂且实用的程序,我们就不得不掌握ARM汇编的伪指令(pseudo-instruction)。千万别把汇编伪操作(directive)与汇编伪指令(pseudo-instruction)弄混了,directive不会被编译器编译为机器指令,但pseudo-instruction会。而pseudo-instruction与指令(instruction)的区别在于,1条instruction与1条机器指令对应,而编译器会把1条pseudo-instruction编译为1条或多条机器指令。

ARM汇编伪指令共4条:ldr、adr、adrl、nop

本文引用地址:https://www.eepw.com.cn/article/201611/322178.htm

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程序中几乎是必然的。


上一页 1 2 下一页

评论


技术专区

关闭