ARM的MOV指令如何使用大数作为第二个操作数?
我刚刚开始学习ARM汇编语言,不太清楚如何使用MOV将立即数传送到寄存器中。
从ARM参考手册和我的教科书中都说MOV指令后面的立即数范围是0-255。但当我在自己的 PC 上使用 ADS 1.2 IDE 进行测试时,指令
MOV R2, #0xFFFFFFFF
执行良好。根据规范,数字 0xFFFFFFFF 是否超出范围?
I just begin to study ARM assembly language, and am not clear about how to use MOV to transfer an immediate number into a register.
From both the ARM reference manual and my textbook, it's said that range of immediate number following MOV instruction is 0-255. But when I test on my own PC in ADS 1.2 IDE, instruction
MOV R2, #0xFFFFFFFF
performs well. Isn't number 0xFFFFFFFF out of range according to the specification?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
请记住,ARM 可以对立即值执行一组特定的操作,作为合并到 ARM 操作码中的桶形移位器的一部分。
这篇小文章对 ARM 汇编器可以用来将大量立即数放入 ARM 指令的小可用空间中的一些技巧进行了最清晰的解释:
文章讨论生成 MVN 操作码以加载立即值的按位补码的特定示例中可能使用的技巧。
这些类型的操作不能用所有立即值来完成,但 ARM 汇编器在这方面据说非常聪明(C 编译器当然也是如此)。如果无法执行移位/补码技巧,则通常会从 PC 相关位置加载该值,或者可能通过从多个指令“构建”该值来加载该值。
Remember that the ARM can perform a certain set of manipulations on the immediate value as part of the barrel shifter that is incorporated into the ARM's opcodes.
This little article has one of the clearest explanations of some of the tricks that an ARM assembler can use to fit a large immediate number into the small available space of an ARM instruction:
The article discusses the trick likely used in your specific example of generating a MVN opcode to load the bitwise complement of the immediate value.
These kinds of manipulation can't be done with all immediate values, but the ARM assemblers are supposedly pretty smart about it (and C compilers certainly are). If no shift/complement tricks can be performed, the value will generally be loaded from a PC-relative location or maybe by 'building up' the value from several instructions.
一条 ARM 指令只能编码一个立即常数,该常数可以表示为 8 位立即值,移位任意 2 的偶次幂。
然而,还有一个
MVN
指令,它类似于MOV
,但反转所有位。因此,虽然MOV R2, #0xFFFFFFFF
无法编码为MOV
指令,但可以编码为MVN R2, #0
。汇编器很可能会为您执行此转换。A single ARM instruction can only encode an immediate constant that can be represented as an 8-bit immediate value, shifted by any even power of two.
However, there is also a
MVN
instruction, which is likeMOV
but inverts all the bits. So, whileMOV R2, #0xFFFFFFFF
cannot be encoded as aMOV
instruction, it can be encoded asMVN R2, #0
. The assembler may well perform this conversion for you.MOV 指令可以接受 imm16 值或 Operator2 值(由于指令长度与内存对齐相反),它必须符合以下任何规则(从 CortexM 指令集手册复制,X 和 Y 是任何十六进制值)
32 位字。
这就是为什么 0xFFFFFFFF 被接受(符合第四条规则)。
如果您希望汇编自己的32位常量,可以使用指令MOVT,该指令写入寄存器的上半部分。
MOV instruction can either accept imm16 value or Operator2 value (due to instruction length opposed to memory alignment), which must conform any of the following rules (copied from CortexM instruction set manual, X and Y is any hex-value):
32-bit word.
This is why 0xFFFFFFFF is accepted (conforms 4th rule).
If you wish to assemble your own 32 bit constant, you can use instruction MOVT, which writes into the upper half of a register.
确定给定常量是否在有效范围内有些困难。
就像 Matthew 已经提到的那样,汇编器通过用类似的否定指令替换给定的指令来帮助您,例如 mov/mvn、cmp/cmn、tst/tne 等。
It's somewhat hard to determine if the given constants are within the valid range.
Like Matthew already mentioned, the assembler lends you hand by replacing given instructions with similar, negating ones, like mov/mvn, cmp/cmn, tst/tne etc.
您可能会看到原始值的符号扩展产生的伪影。如果您用来查看反汇编的工具将 0..255 作为有符号字节处理,那么当它将其加载到更大的 int 类型(或寄存器)时,它将用原始符号位填充所有高位价值。或者换句话说,如果 0xFF 是有符号字节,则其十进制值为 -1。将其放入 32 位寄存器中,十六进制将类似于 0xFFFFFFFF,而其十进制值仍为 -1。
尝试使用未设置高位的值,例如 0x7F。由于未设置符号位,我猜测当加载到较大的 int 类型寄存器或字段时,它将用零填充高位。
编译器/汇编器也有可能截断您提供的任何值。我认为这是一个源代码错误,但汇编程序是有趣的野兽。如果给它 0x7FF,它会编译为 0x7FF(未截断,且大于 0..255)还是 0xFFFFFFFF(截断为 0..255,有符号字节)?
You may be seeing artifacts from sign-extension of the original value. If the tools you're using to view the disassembly handles the 0..255 as a signed byte, then when it loads it into a larger int type (or register) it will fill all the upper bits with the sign bit of the original value. Or to put it another way, if 0xFF is a signed byte its decimal value is -1. Put that into a 32 bit register and the hex will look like 0xFFFFFFFF, and its decimal value is still -1.
Try using a value without the high bit set, such as 0x7F. Since the sign bit is not set, I'm guessing it will fill the upper bits with zero when loaded into a larger int type register or field.
It's also possible that the compiler/assembler truncates whatever value you provide. I'd consider it a source code error, but assemblers are funny beasts. If you give it 0x7FF, does it compile to 0x7FF (not truncated, and larger than 0..255) or to 0xFFFFFFFF (truncated to 0..255, signed byte)?
如果你想将 0xffffffff 移动到寄存器,你总是可以这样做:
因为 0xffffffff 是 -1 的二进制补码表示
If you want to move 0xffffffff to a register you can always do:
because 0xffffffff is the twos-complement representation of -1
一种可能性是 ARM 汇编器丢弃数字的有效位并仅使用最低的 FF。
MOV 指令是许多 CPU 指令集中的主要指令,通常由汇编器解析目标寄存器的大小和提供的立即值。
例如,x86 集中的以下 MOV 指令是
One possibility is that the ARM assembler throws away the significant bits of the number and uses only the lowest FF.
The MOV instruction is a staple in many CPU instruction sets, and usually the assembler resolves the size of the target register and the immediate value being supplied.
For example the following MOV instructions from the x86 set are