将 .word 更改为 .equ 时遇到问题

Having issues changing .word to .equ

我正在做这个关于微控制器的实验 class(我们使用的是 FRDM-KL25Z128),我在使用指令 .word 和 .equ 时遇到了问题

首先,我们教授给我们的示例代码是让 FRDM 板上的红色 LED 闪烁,代码如下:http://www.dca.fee.unicamp.br/cursos/EA871/2s2016/UW/codes/exp2.s

在修改代码使其同时闪烁三个 LED 并发出白光之前,我们有一些问题要回答,但这不是这里的问题。

在代码的底部,所有寄存器地址都使用 .word 指令定义(因为我们使用的是 32 位字)。其中一个问题是回答我们是否可以将指令 .word 替换为 .equ.

我的第一个想法是,是的,我可以用 .word 代替 .equ,因为最后一条指令是为标签分配一个常量值,而且由于我不想更改这些标签的值,.equ 应该可以用。

但是当我测试它时,通过评论这些行:

SIM_SCGC5: @ Endereço do SIM_SCGC5
.word 0x40048038

并写作:

.equ SIM_SCGC5, 0x40048038

应该将值 0x40048038 分配给 SIM_SCGC5 标签。但是代码不起作用,我会在以下行中收到错误消息:

ldr     r3,SIM_SCGC5

说:

invalid offset, value too big

所以我不确定是我在 .equ 指令中搞砸了,还是默认情况下,.equ 分配的值占用的位数超过了 LDR 可以处理的位数。

这里可能是什么问题?

一些注意事项:

.word assembles 4 个字节到输出文件的当前位置。如果你想把数据存储在那里,你必须从你放置它的地址加载它。如果你在它前面放一个标签,这个符号的值就是地址。

.equ 定义了一个 assemble 时间常数,用作未来表达式中的值。符号的值是常量,没有地址,目标文件中也没有字节。所以你可以使用用 .equ 定义的东西作为其他指令的直接操作数,比如 add。您不能将它与 loads/stores.

一起使用

将立即数放入寄存器不同于从固定地址加载。 Use ldr r3, =SIM_SCGC5 to have the assembler generate whatever sequence of instructions 它决定最好在 r3 中生成该常量。他们混淆地为这个伪操作选择了与实际加载指令相同的助记符。

另见 Why use LDR over MOV (or vice versa) in ARM assembly?。这可能是现有 SO 问题的重复,但我还没有找到理想的重复目标。

当用 .equ 定义 SIM_SCGC5 时,我认为指令 ldr r3, SIM_SCGC5 会尝试从该绝对地址加载。您得到的错误是因为无法将其编码为一条指令。

它将使用mov or movn with a shifted/rotated immediate if possible,否则从附近的常量池回退到PC相关负载。我认为设置低16位和高16位的两条指令也是可能的。

另见 Constants on ARM, and various SO questions like

.此处使用的单词

ldr r0,hello
nop
nop
nop
hello: .word 0x12345678

类似于

unsigned int word = 0x12345678

你好:是一个标签。word与它无关。只是意味着此时我想为地址使用标签,可以将代码或数据放在那里。就像 C 中的 unsigned int 一样,你在程序中分配了一些 space。

.equ 虽然就像一个定义,但您没有分配 space,您只是为该字符串定义一些替换。

arm-none-eabi-as so.s -o so.o
arm-none-eabi-objdump -D so.o
...
00000000 <hello-0x10>:
   0:   e59f0008    ldr r0, [pc, #8]    ; 10 <hello>
   4:   e1a00000    nop         ; (mov r0, r0)
   8:   e1a00000    nop         ; (mov r0, r0)
   c:   e1a00000    nop         ; (mov r0, r0)
00000010 <hello>:
  10:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

如果我加上这个

.equ JELLO, 0x22
ldr r0,hello
nop
nop
nop
hello: .word 0x12345678

没有变化,如果我这样做

.equ JELLO, 0x12345678
ldr r0,JELLO

.equ 是一个类似替换的定义。

ldr r0,0x12345678

so.s: Assembler messages:
so.s:2: Error: internal_relocation (type: OFFSET_IMM) not fixed up

现在如果我们想要 r0 中的 VALUE,那么这是一个语法问题。

mov r0,#0x12345678

我们不能在 ARM 中做到这一点,指令是 16 位或 32 位,仅此而已,因此您不能在 32 位中有 32 位立即数、操作码和寄存器等。所以必须给出一些东西,取决于指令集和变化的立即数在几个到最多可能 11 或 13 位之间,正常的 arm mov 指令全尺寸 arm 指令,如 9 个有效位。

so.s: Assembler messages:
so.s:3: Error: invalid constant (12345678) after fixup

有趣的技巧是回去加载并询问汇编程序,我想要标签的地址而不是标签上的东西。

ldr r0,=hello
nop
nop
nop
hello: .word 0x12345678
.word 0,1,2,3

汇编程序为我们分配了一个字,它可以在其中找到 space

00000000 <hello-0x10>:
   0:   e59f001c    ldr r0, [pc, #28]   ; 24 <hello+0x14>
   4:   e1a00000    nop         ; (mov r0, r0)
   8:   e1a00000    nop         ; (mov r0, r0)
   c:   e1a00000    nop         ; (mov r0, r0)
00000010 <hello>:
  10:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000
  14:   00000000    andeq   r0, r0, r0
  18:   00000001    andeq   r0, r0, r1
  1c:   00000002    andeq   r0, r0, r2
  20:   00000003    andeq   r0, r0, r3
  24:   00000010    andeq   r0, r0, r0, lsl r0

注意,它没有将地址 0x10 处的内容加载到寄存器中,而是将地址 0x24 处的内容加载到它为我们添加的位置,并在该位置向 hello 提供地址,即 0x10,因此 r0通过添加等号将得到 0x10 而不是 0x12345678。有点像在 C 中删除一维指针上的星号,你得到的是指针的地址,而不是指针指向的东西。

所以知道这个 ldr rx,=something 意味着我想要那个标签的地址,如果我们只是把一个地址放在那里而不是一个代表地址的标签呢?

ldr r0,=0x87654321
nop
nop
nop
hello: .word 0x12345678
.word 0,1,2,3

恰好有效

0000000 <hello-0x10>:
   0:   e59f001c    ldr r0, [pc, #28]   ; 24 <hello+0x14>
   4:   e1a00000    nop         ; (mov r0, r0)
   8:   e1a00000    nop         ; (mov r0, r0)
   c:   e1a00000    nop         ; (mov r0, r0)
00000010 <hello>:
  10:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000
  14:   00000000    andeq   r0, r0, r0
  18:   00000001    andeq   r0, r0, r1
  1c:   00000002    andeq   r0, r0, r2
  20:   00000003    andeq   r0, r0, r3
  24:   87654321    strbhi  r4, [r5, -r1, lsr #6]!

所以这并不能证明你可以在任何你想要的基于 gnu 汇编器的指令集中用 .equ 盲目地替换 .word,但是由于运气不好,你几乎可以通过修复语法来实现

.equ JELLO,0x12345678
ldr r0,=JELLO
nop
nop
nop

好了

00000000 <.text>:
   0:   e59f0008    ldr r0, [pc, #8]    ; 10 <JELLO-0x12345668>
   4:   e1a00000    nop         ; (mov r0, r0)
   8:   e1a00000    nop         ; (mov r0, r0)
   c:   e1a00000    nop         ; (mov r0, r0)
  10:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

一般来说 any/other 指令将 .word 设置为 .equ 并加载你需要更改为 mov

.equ JELLO,0x12
ldr r0,hello
mov r0,#JELLO
nop
nop
nop
hello:
.word 0x12


00000000 <hello-0x14>:
   0:   e59f000c    ldr r0, [pc, #12]   ; 14 <hello>
   4:   e3a00012    mov r0, #18
   8:   e1a00000    nop         ; (mov r0, r0)
   c:   e1a00000    nop         ; (mov r0, r0)
  10:   e1a00000    nop         ; (mov r0, r0)
00000014 <hello>:
  14:   00000012    andeq   r0, r0, r2, lsl r0

使用任何语法(x86 中的 mov、move 等)mov 用于立即加载和移动,因此您只需要知道是否需要修改任何语法以从标签切换到立即(删除也许是某种形式的 word ptr hello 语法))

请注意,我使用的是上面遗留的 gnu 汇编器语法,所以使用那个

.equ JELLO,0x12
mov r0,JELLO

给予

so.s: Assembler messages:
so.s:4: Error: immediate expression requires a # prefix -- `mov r0,JELLO'

但是如果我们使用统一的语法,我猜它不再关心井号了。

.syntax unified
.equ JELLO,0x12
mov r0,JELLO
mov r0,#JELLO

00000000 <.text>:
   0:   e3a00012    mov r0, #18
   4:   e3a00012    mov r0, #18

不是粉丝,应该让它更容易(更懒惰),只会让它变得更糟......这就是为什么我很少使用它,除非我必须......YMMV。