我们知道ARM CPU中有一条被广泛使用的指令LDR,它主要是用来从存储器(确切地说是地址空间)中装载数据到通用寄存器。但不论是ARMASM还是GNU ARM AS,都提供了一条与之同名的伪指令LDR,而在实际中使用该伪指令的情况也较多,那他们有什么不同呢?下面我谈谈我的理解。
LDR
.equ STACK_SIZE, 0x00001000
ldr sp, = STACK_BASE
ldr sl, = STACK_BASE - STACK_SIZE
ldr pc, = entry
mov sp, #0x0c002000
mov sl, #0x0c001000
mov pc, #0x0c000000
demo.s: Assembler messages:
demo.s:2: Error: invalid constant -- `mov sp,#0x0c002000'
demo.s:3: Error: invalid constant -- `mov sl,#0x0c001000'
mov sp, #0x0c000000
add sp, sp, #0x00002000 或者: .text
mov sp, #0x0c000000
orr sp, sp, #0x00002000
感觉很狡猾是吧,呵呵。但是要注意它和方法一的一大区别:需要多条指令。那么在一些对指令数目有限制的场合就无法使用它,比如异常向量表处要做长跳转(超过±32MB)的话就只能用方法一;还有就是在同步事务中该操作不是原子的,因此可能需要加锁。
arm-elf-as -o demo.o demo.s
arm-elf-objdump -D demo.o
0: e59fd004 ldr sp, [pc, #4] ; c <.text+0xc>
4: e59fa004 ldr sl, [pc, #4] ; 10 <.text+0x10>
8: e59ff004 ldr pc, [pc, #4] ; 14 <.text+0x14>
c: 0c002000 stceq 0, cr2, [r0]
10: 0c001000 stceq 0, cr1, [r0]
14: 00000000 andeq r0, r0, r0
Disassembly of section .data:
由于entry还没连上目标地址,objdump反汇编会认为是0,我们先不管它。另外两条LDR伪指令变成了实际的LDR指令!但目标很奇怪,都是[pc, #4]。那好我们看看[pc, #4]是什么。
mov r0, #0x10000004
add r0, r0, #0x000ff000
add r0, r0, #0x00000c00
...
原因不详。
作者:Walzer
日期:2005.2.4
同事遇到这样一个问题:
在eVC编译出的汇编代码中我看到这样的语句:
mov r2, #0xFF, 28 和 orr r2, r2, #0xB
这样得到的结果时 r2=#0xffb ,
他试图更直接一点优化成一句:MOV r2,#0xffb
但是这样之后编译就出了问题:error A0092: no immediate rotate operand can be created: 4091
------------------------------------我是无辜的分割线--------------------------------
在 mov r2,#0xffb 这句中,不是MOV的用法出错,而是立即数用法出错。
立即数的用法定义在Arm Architechture Reference Manual(简称ARMARM)的A5-4页开始
很重要的一段:
An immdediate operand value is formed by rotating an 8-bit constant (in a 32-bit word) by an even number of bits (0,2,4,8,26,28,30). Therefore, each instruction contains an 8-bit constant and a 4-bit rotate to be applied to that constant.
Some valid constants are:
0xFF, 0x104, 0xFF0, 0xFF00, 0xFF000, 0xFF000000, 0xF000000F
Some invalid constants are:
0x101, 0x102, 0xFF1, 0xFF04, 0xFF003, 0xFFFFFFFF, 0xF000001F
而在下面的A5-6页中提到
shifter_operand = immed_8 Rotate_Right (rotate_imm * 2)
以及
Some values of
所以,综上所述,首先解释清楚了 mov r2,#0xFF,28 一句。28并不是第三个操作数,而是和0xFF并在一起作为立即数使用,将0xFF循环右移28位。而这里必须强调右移XX位必须是个偶数,因为它将等于rotate_imm*2,那么在该指令的机器码中rotate_imm = 14, 也就是在32-BIT的机器码中第11到第8 bit = 1110B
然后再来看 mov r2,#0xFFB 的出错情况
0xFFB = 111111111011B,很显然按照 shifter_operand = immed_8 Rotate_Right (rotate_imm * 2) 的公式, shifter_operand = 0xFFB时无法得到有效的 immed_8 和 rotate_imm, 所以编译出现错误 error A0092: no immediate rotate operand can be created: 4091 也可以理解了,它应该是说无法生成rotate_imm,实际上immed_8也是无法生成的。
关于立即数如何分解成immed_8和rotate_imm,可以参考上面给出的valid constants和invalid constants,简而言之,如果该立即数可以分解成一个8-bit的二进制数循环右移偶数位,那么这个立即数是有效的,反之无效。
在上面的例子中,想要得到 r2 = 0xFFB 那么在汇编里就必须走两步了,一步是无论如何无法到达的。