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

jum4pzuy  于 2023-11-19  发布在  其他
关注(0)|答案(4)|浏览(105)

我正在浏览这个教程:http://www.cl.cam.ac.uk/freshers/raspberrypi/tutorials/os/ok01.html
第一条装配线是:

ldr r0,=0x20200000

字符串
第二个是:

mov r1,#1


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

wf82jlnq

wf82jlnq1#

这是一个窍门/捷径。比如说

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来鼓励汇编程序找到一个地方。

ntjbwcob

ntjbwcob2#

一个简短的回应,来自一个更接近你水平的人,希望它能有所帮助:在ARM中,指令有32位。一些位用于识别操作,一些用于操作数,在MOV指令的情况下,一些可用于立即值(例如#1)。
正如你所看到的here(第33页),只有12位可用于立即数。不是使用每个位作为数字(范围从0到2^12-1~4095),而是通过右旋转(ROR)前8位,最后4位指定的量的两倍来计算立即数。即immediate = first 8 bits ROR 2*(last four bits)
通过这种方式,我们可以获得比0到4095更广泛的数字范围(参见第34页可能的立即数的简要总结)。但是请记住,对于12位,仍然只有4096个可能的值可以指定。
如果我们的数字不能转换成像前一个那样的指令(257不能表示为8位旋转2次任何4位),那么,我们必须使用LDR r 0,=257
在这种情况下,编译器将数字257保存在内存中,靠近程序代码,因此它可以相对于PC进行寻址,并从内存中加载它,就像dwell详细解释的那样。
注意:如果你按照那个教程,那么当你尝试用mov r 0,#257 'make'时,你会得到一个错误,你必须手动尝试ldr r 0,=257。

qhhrdooz

qhhrdooz3#

虽然其他答案都很好,但我想我可以简化答案。

ldr= LoaD寄存器
mov= MOVE

两者都有效地做同样的事情,但以不同的方式。
这种区别就像

#define CONST 5

字符串

int CONST = 5;


用C语言编写。

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

  • 12位不足以存储像32位内存地址这样的巨大数字。
  • 前8位ROR2 *(最后4位)不能表示任何数字,即使在12位范围内。

另一方面,ldr是通用的(主要是由于编译器优化)。它是这样工作的(如反汇编例程所示)

  • 如果值可以用12位和前8位ROR2 *(最后4位)格式表示,则编译器将其更改为伴随值的mov指令。
  • 否则,该值将作为数据保存,加载到RAM中的某个位置。通过使用程序计数器的偏移量从内存访问,将其加载到所需的寄存器中。

希望能帮上忙。

ufj5ltwl

ufj5ltwl4#

这是这个问题唯一的答案,所有其他的答案都令人困惑。

MOV:* 要放入的立即值,编码T1时允许的值范围为0-255 *
LDR:* 对于编码T1,偏移的允许值是0-1020范围内的4的倍数。*

ARMv 6-M说明书:https://developer.arm.com/documentation/ddi0419/latest/

相关问题