为什么在 ARM 汇编中使用 LDR 而不是 MOV(反之亦然)?

问题描述 投票:0回答:4

我正在浏览本教程:http://www.cl.cam.ac.uk/freshers/raspberrypi/tutorials/os/ok01.html

第一条装配线是:

ldr r0,=0x20200000

第二个是:

mov r1,#1

我认为

ldr
是用于将值从内存加载到寄存器中。但似乎
=
意味着 0x20200000 是一个值而不是内存地址。这两行似乎都在加载绝对值。

assembly arm immediate-operand
4个回答
26
投票

这是一个技巧/捷径。举例来说

ldr r0,=main

会发生的情况是汇编器会在指令附近但在指令路径之外分配一个数据字

ldr r0,main_addr
...
b somewhere
main_addr: .data main

现在将该技巧扩展到常量/立即数,特别是那些无法适合立即移动指令的内容:

top:
add r1,r2,r3
ldr r0,=0x12345678
eor r1,r2,r3
eor r1,r2,r3
b top

先组装再拆卸

00000000 <top>:
   0:   e0821003    add r1, r2, r3
   4:   e59f0008    ldr r0, [pc, #8]    ; 14 <top+0x14>
   8:   e0221003    eor r1, r2, r3
   c:   e0221003    eor r1, r2, r3
  10:   eafffffa    b   0 <top>
  14:   12345678    eorsne  r5, r4, #125829120  ; 0x7800000

你会看到汇编器已经为你添加了数据字,并将 ldr 更改为你的 pc 相对值。

现在,如果您使用适合 mov 指令的立即数,那么可能取决于汇编器,当然还有我正在使用的 gnu,它会将其变成我的 mov

top:
add r1,r2,r3
ldr r0,=0x12345678
ldr r5,=1
mov r6,#1
eor r1,r2,r3
eor r1,r2,r3
b top


00000000 <top>:
   0:   e0821003    add r1, r2, r3
   4:   e59f0010    ldr r0, [pc, #16]   ; 1c <top+0x1c>
   8:   e3a05001    mov r5, #1
   c:   e3a06001    mov r6, #1
  10:   e0221003    eor r1, r2, r3
  14:   e0221003    eor r1, r2, r3
  18:   eafffff8    b   0 <top>
  1c:   12345678    eorsne  r5, r4, #125829120  ; 0x7800000

所以它基本上是一个打字快捷方式,了解你正在赋予汇编器找到一个地方来粘贴常量的能力,它通常做得很好,有时会抱怨,不确定我是否见过它未能安全地做到这一点。有时您需要在代码中使用 .ltorg 或 .pool 来鼓励汇编器找到位置。


16
投票

来自与您水平更接近的人的简短回复,希望它有所帮助:在 ARM 中,指令有 32 位。有些位用于标识操作,有些用于操作数,对于 MOV 指令,有些位可用于立即值(例如,#1)。

正如您在此处(第33页)所看到的,只有12位可用于立即值。该指令不是使用每个位作为数字(范围从 0 到 2^12-1~4095),而是通过将前 8 位右移 (ROR) 最后 4 位中指定的数量的两倍来计算立即数。也就是说,

immediate = first 8 bits ROR 2*(last four bits)

通过这种方式,我们可以获得比 0 到 4095 更广泛的数字(有关可能的立即数的简要总结,请参阅第 34 页)。但请记住,对于 12 位,仍然只能指定 4096 个可能的值。

万一我们的数字不能像前面那样转换成指令(257不能表示为8位循环任意4位两次),那么,我们就必须使用LDR r0, =257

在这种情况下,编译器将数字 257 保存在内存中,靠近程序代码,因此可以相对于 PC 进行寻址,并从内存中加载它,正如 dwelch 详细解释的那样。

注意:如果您按照该教程进行操作,那么当您尝试使用 mov r0, #257 进行“make”时,您将收到错误,并且您必须手动尝试 ldr r0, =257。


11
投票

与其他答案一样好,我想我也许可以简化答案。

ldr=负载寄存器

mov = 移动

两者实际上都以不同的方式做同样的事情。

差异很像

#define CONST 5

int CONST = 5;

C语言。

mov 非常快,因为它具有直接存储为指令一部分的伴随值(以上面答案中描述的 12 位格式)。由于其存储值的方式,它有一些限制。为什么?因为

  • 12 位不足以存储像 32 位内存地址这样的巨大数字。
  • 前 8 位 ROR 2 *(后 4 位)不能代表任何数字,即使在 12 位范围内也是如此。
另一方面,

ldr是通用的(主要是由于编译器优化)。工作原理是这样的(如拆解例程所示)

  • 如果该值可以用 12 位和前 8 位 ROR 2 *(后 4 位)格式表示,则编译器会将其更改为伴随该值的 mov 指令。

  • 否则,该值将作为数据保存,加载到 RAM 中的某个位置。然后通过使用程序计数器的偏移量从内存访问来将其加载到所需的寄存器中。

希望对您有帮助。


0
投票

这是应该回答问题的唯一方法,所有其他答案都令人困惑

MOV要放入 中的立即值。对于编码 T1

,允许值的范围是 0-255

LDR对于编码 T1,允许的偏移量值是 0-1020 范围内的四的倍数。

ARMv6-M 手册:https://developer.arm.com/documentation/ddi0419/latest/

© www.soinside.com 2019 - 2024. All rights reserved.