我参加了这个关于微控制器的实验班(我们使用的是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)的最爱,但我不想在没有上下文的情况下抛出一个问题。
答案 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。