有问题改变.word到.equ

时间:2016-08-31 00:50:37

标签: assembly arm microcontroller directive nxp-microcontroller

我参加了这个关于微控制器的实验班(我们使用的是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

说:

  

无效偏移,值太大

所以我不确定我是否搞砸了.equ指令,或者默认情况下,.equ指定一个比LDR可以处理更多位的值。

这里有什么问题?

一些注意事项:

  • 我开始使用微控制器与汇编代码,所以我仍然缺少很多信息,比如硬件限制。所以我可能在不知情的情况下写了一堆随机单词。

  • 很抱歉做了很长时间的准备。我知道长篇文章不是Stack Overflow(或一般的Stack Exchange)的最爱,但我不想在没有上下文的情况下抛出一个问题。

2 个答案:

答案 0 :(得分:4)

这里使用的

.word

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

类似于

unsigned int word = 0x12345678

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

.equ虽然就像是一个你没有分配空间的定义,但你只是为该字符串定义了一些替换。

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指令全长臂指令,如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

汇编程序为我们分配了一个可以找到空格的单词

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,=某事意味着我想要那个标签的ADDRESS,如果我们只是在那里放一个地址而不是一个代表地址的标签怎么办?

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。但是由于运气不好你几乎可以通过修复语法来实现这个目标

.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

一般情况下,任何/其他指令集.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中的mov,move等用于加载和mov立即执行,因此您只需要知道如果需要语法修改从标签切换到立即(删除某些表单)也许是ptr你好语法的语法))

注意我正在使用上面的遗留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。

答案 1 :(得分:2)

.word将4个字节组装到当前位置的输出文件中。如果您希望存储数据,则必须从放置数据的地址加载数据。如果在其前面放置标签,则符号的值为地址。

.equ定义了一个汇编时常量,用作未来表达式中的值。符号的值是常量,没有地址,并且目标文件中没有字节。因此,您可以使用.equ定义的内容作为其他指令的直接操作数,例如add。您无法在装载/存储中使用它。

将直接常量放入寄存器与从固定地址加载不同。 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问题的重复,但我还没有找到理想的重复目标。

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

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

另请参阅Constants on ARM以及各种SO问题,例如Arm cortex-m3 mov and ldr